Cloudflare Workers 获取静态资源
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 绑定。
实施步骤
-
创建 KV 命名空间:通过 Wrangler 命令创建一个新的 KV 库
APP_IMAGES,并在wrangler.json中进行绑定。{ "kv_namespaces": [ // ... { "binding": "APP_IMAGES", "id": "...", "preview_id": "..." } ] } -
上传图片:我编写了一个 Node.js 脚本 (
scripts/upload-images.mjs),它遍历public目录下的所有图片,并通过wrangler kv key put命令将它们以二进制形式上传到APP_IMAGES中。 -
改造 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 }, }); } // ... 其他路由逻辑
这个方案最终成功地解决了所有问题,预览图可以稳定、快速地提供服务。
结论与启示
这次经历告诉我:
- 标准方案并非永远可靠:即使是文档中最推荐的
site/ASSETS方案,也可能在特定环境或版本下遇到问题。当一条路走不通时,需要灵活变通。 - 深入理解平台工具:Cloudflare 生态系统提供了多种工具(Workers, KV, R2 等),深入理解它们的特性和限制,是构建复杂应用的基石。当
ASSETS不可用时,KV 成为了完美的替代品。 - 化繁为简:我将一个复杂的静态资源部署问题,最终分解为“上传”和“提供”两个简单的步骤,通过脚本和几行代码清晰地实现了目的,降低了对平台“黑盒”行为的依赖。
希望我的经验能对你有所帮助!