Cloudflare Workers 获取静态资源

cloudflare 服务器部署 DevOps

Cloudflare Workers 图片处理踩坑记:从静态资源到 KV 存储的演进

在开发一个基于 Cloudflare Workers 的 AI 头像生成器时,我遇到了一个看似简单却一波三折的核心问题:如何为用户提供高效、可靠的风格预览图?

这个过程让我对 Cloudflare Workers 的静态资源处理机制有了意想不到的深入理解。本文将复盘我经历的四次方案迭代,希望能为遇到类似问题的朋友提供一些参考。

方案一:site 配置与 env.ASSETS 的美好初衷

根据 Cloudflare 的官方文档,处理静态资源最标准的方式是在 wrangler.json 中配置 site 字段,指向一个包含静态文件的目录(如此处的 public)。

{
  "name": "app-avatar-generator",
  "main": "src/index.ts",
  "site": {
    "bucket": "./public"
  },
  // ...其他配置
}

理论上,wrangler deploy 会将 public 目录下的文件上传,并通过一个特殊的 env.ASSETS 服务绑定在 Worker 中。我的代码也相应地进行了适配:

// ...
if (path.startsWith('/api/')) {
  // 处理 API
} else {
  // 其他所有请求都交给 ASSETS 服务处理
  return env.ASSETS.fetch(request);
}

遇到的问题:

这套“标准”方案在实际部署后,却出现了致命的 Error 1101: Worker threw exception 错误。经过排查,我发现即使部署日志显示静态资源已上传,但在生产环境中 env.ASSETS 对象依然是 undefined,导致代码在调用 env.ASSETS.fetch 时直接崩溃。这指向了 Wrangler 工具或 Cloudflare 平台在特定场景下的一个深层问题或 bug。

方案二:外部图床的挑战

既然内部的路走不通,我自然想到了外部 CDN。尝试将图片上传到七牛云图床,并直接在前端引用其链接。

遇到的问题:

七牛云提供的测试域名是 http 协议。而在 https 协议的 Worker 页面中请求 http 资源,会因“混合内容(Mixed Content)”被浏览器安全策略阻止,导致图片无法加载。虽然可以通过配置付费域名解决,但这增加了项目的复杂性和依赖性。

方案三:Base64 内联的简单与粗暴

为了消除所有外部依赖,我尝试了一个最直接的方法:将图片转为 Base64 编码,然后直接嵌入到 Worker 的代码中,与 HTML 一同返回。

遇到的问题:

这个方案在图片很少、很小的情况下是可行的。但随着预览图的增加,我的 Worker 脚本大小迅速膨胀,很快就触发了 Cloudflare 免费套餐对单个 Worker 脚本的体积限制(例如 1MB 或 3MB),导致部署失败。此路不通。

最终方案:KV 力挽狂澜,稳定可靠

在排除了以上所有方案后,我最终选择了 Cloudflare 的另一个原生利器:KV 存储。

KV 通常用于存储文本类键值对,但它同样支持直接存储二进制数据(最大支持 25MB),对于我的预览图来说绰绰有余。这个方案彻底绕开了有问题的 env.ASSETS 绑定。

实施步骤

  1. 创建 KV 命名空间:通过 Wrangler 命令创建一个新的 KV 库 APP_IMAGES,并在 wrangler.json 中进行绑定。

    {
      "kv_namespaces": [
        // ...
        {
          "binding": "APP_IMAGES",
          "id": "...",
          "preview_id": "..."
        }
      ]
    }
  2. 上传图片:我编写了一个 Node.js 脚本 (scripts/upload-images.mjs),它遍历 public 目录下的所有图片,并通过 wrangler kv key put 命令将它们以二进制形式上传到 APP_IMAGES 中。

  3. 改造 Worker:修改 src/index.ts,在主 fetch 函数中加入处理图片请求的逻辑。

    // ...
    // Handle image requests from KV
    if (path.endsWith('.png') || path.endsWith('.ico')) {
        const key = path.substring(1); // 移除前导斜杠
        const image = await env.APP_IMAGES.get(key, { type: 'arrayBuffer' });
    
        if (image === null) {
            return new Response(`Image ${key} not found in KV`, { status: 404 });
        }
    
        const contentType = path.endsWith('.png') ? 'image/png' : 'image/x-icon';
        return new Response(image, {
            headers: { 'Content-Type': contentType },
        });
    }
    // ... 其他路由逻辑

这个方案最终成功地解决了所有问题,预览图可以稳定、快速地提供服务。

结论与启示

这次经历告诉我:

  1. 标准方案并非永远可靠:即使是文档中最推荐的 site/ASSETS 方案,也可能在特定环境或版本下遇到问题。当一条路走不通时,需要灵活变通。
  2. 深入理解平台工具:Cloudflare 生态系统提供了多种工具(Workers, KV, R2 等),深入理解它们的特性和限制,是构建复杂应用的基石。当 ASSETS 不可用时,KV 成为了完美的替代品。
  3. 化繁为简:我将一个复杂的静态资源部署问题,最终分解为“上传”和“提供”两个简单的步骤,通过脚本和几行代码清晰地实现了目的,降低了对平台“黑盒”行为的依赖。

希望我的经验能对你有所帮助!