Workers 没有文件系统,所有文件都得存 R2。聊聊用户上传和 CMS 图片管理的完整方案。
为什么用 R2
Cloudflare Workers 没有文件系统,不能像 Node.js 那样 fs.writeFile()。R2 是 Cloudflare 的对象存储,兼容 S3 API:
- 全球分布式,边缘节点就近访问
- 无出口流量费(对比 S3 的 0.09/GB)
- 与 Workers 天然集成,零配置
分桶策略
RuiTool AI 用两个 R2 Bucket 隔离不同用途的文件:
| Bucket | 用途 | 访问权限 |
|---|---|---|
IMAGES_R2 | CMS 图片(运营上传) | 公开读取 |
USER_UPLOADS_BUCKET | 用户上传的文件 | 需认证 |
分桶的好处:
- 权限隔离:CMS 图片公开访问,用户文件需要鉴权
- 成本隔离:可以分别设置生命周期策略
- 数据隔离:不会因为用户上传影响 CMS 内容
CMS 图片上传
CMS 图片上传流程:
| |
关键设计:
- magic bytes 校验:用
file-type库检查文件的实际内容,而不是信任Content-Type头 - 唯一文件名:
{cuid2}-{sanitized-filename}.ext,避免冲突 - 文件名清理:
sanitizeFilename去掉路径分隔符和特殊字符 - 记录到数据库:媒体库需要可搜索、可管理
用户文件上传
用户上传的文件存到 USER_UPLOADS_BUCKET,按用户 ID 隔离:
| |
路径格式:users/{userId}/{fileId}-{filename},每个用户的文件在独立前缀下。
文件类型校验
| |
不要信任 file.type(前端可以伪造),必须用服务端 magic bytes 检测。
通过 API 访问文件
R2 的文件不直接暴露 URL,而是通过 API 路由代理:
| |
用户文件同理,但需要先验证 Session。
总结
- 两个 Bucket 隔离 CMS 和用户文件
- 用
file-type做 magic bytes 校验,不信任前端 MIME type - 文件名用
cuid2保证唯一性,sanitizeFilename防止路径穿越 - 文件通过 API 路由代理访问,可以做权限控制和缓存策略
