Storage (R2)
基于 Cloudflare R2 的私有对象存储。普通素材和头像都走 浏览器直传:文件字节不会经过 Worker,Worker 只负责签发短期 PUT URL、校验完成结果和提供读取路由。
上传链路
- 浏览器调用
createUploadUrl,服务端校验声明的 MIME/大小,生成assets/<userId>/<uuid>或avatars/<userId>/<uuid>,并创建status='pending'的 asset 行。头像用source='avatar'区分,不会混入普通素材库。 - Worker 用 R2 S3 API credentials 签发 15 分钟有效的预签名 PUT URL;
Content-Type被写入 SigV4 签名。浏览器随后直接 PUT 到 R2。 - 浏览器调用
completeUpload。服务端用 R2 bindingHEAD对象,核对实际大小和Content-Type,再以 compare-and-set 把行切到ready。
所有 key 都由服务端生成且每次上传不同。user.image 仍是稳定读取路径
/api/avatars/<userId>?v=<assetId>;读取路由通过 asset 行解析真正的随机 R2 key。
读取路由还兼容旧版本的数字时间戳 ?v=<Date.now()>,此时回退读取原固定 key,因而上线
不需要迁移现有头像;用户下次上传后会自然切到新格式。
计划任务会清理两类对象:超过 1 小时仍未完成的直传,以及保留 7 天后已被替换的头像历史。
当前头像永远不会因年龄被删除;异步生成视频虽然也使用 pending,但 source='generated'
不会进入直传清理范围。
本地配置
R2 binding 在 miniflare 测试里可直接使用,但浏览器直传需要真实的 R2 S3 endpoint 和
credentials。在 .dev.vars 配齐以下五项;五项全空时直传会优雅关闭:
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=auto
AWS_ENDPOINT_URL_S3=https://<accountId>.r2.cloudflarestorage.com
S3_BUCKET=flarestarter-filesAPI token 只授予目标 bucket 的 Object Read & Write 权限。AWS_SECRET_ACCESS_KEY 不要写进
wrangler.jsonc 或提交到 Git。
Bucket CORS(必需)
浏览器访问预签名 URL 仍受 CORS 约束。复制 r2-cors.example.json,把
https://example.com 改成真实站点 origin(只有 scheme、host 和可选 port,不要带路径),
然后对每个环境的 bucket 应用:
npx wrangler r2 bucket cors set flarestarter-files --file r2-cors.example.json
npx wrangler r2 bucket cors set flarestarter-files-staging --file r2-cors.example.json
npx wrangler r2 bucket cors set flarestarter-files-prod --file r2-cors.example.json规则只开放 PUT 和 Content-Type。没有这条 CORS policy 时,签名本身即使正确,浏览器
直传也会失败。
部署
首次部署先创建三个 bucket:
npx wrangler r2 bucket create flarestarter-files
npx wrangler r2 bucket create flarestarter-files-staging
npx wrangler r2 bucket create flarestarter-files-prod再为相应命名环境写入 S3 credentials。生产环境示例:
npx wrangler secret put AWS_ACCESS_KEY_ID --env production
npx wrangler secret put AWS_SECRET_ACCESS_KEY --env production
npx wrangler secret put AWS_REGION --env production
npx wrangler secret put AWS_ENDPOINT_URL_S3 --env production
npx wrangler secret put S3_BUCKET --env production最后应用上面的 CORS policy。读取仍经过 Worker 的 /api/assets/* 和 /api/avatars/*,
无需把 bucket 设为 public。
安全边界
- client 的 MIME/大小声明先按头像或素材白名单校验;key 永远由服务端生成。
- SigV4 把 PUT method、对象 key、过期时间和
Content-Type固定下来。 completeUpload不接受 client 提供的 kind,而从 DBsource判定 avatar;并以 R2 HEAD 的真实 metadata 做最终大小/类型校验。- 普通素材读取和删除都排除 avatar 行;素材读取还要求 owner session。