R2 计费

你可以简单记成:

总共就三笔钱:存储 + 操作次数 +(如果用 IA)取回费。 出网到互联网(Egress)是免费的。

存储费用(Standard storage)

  • 单价:$0.015 / GB-month

  • Free tier:每月前 10 GB-month 免费

  • “GB-month” = 这一个月里平均占用的 GB 数。

    • 整月 5GB → 5 GB-month。

操作次数费用

  • Class A(写 / 列目录)

    • 单价:$4.50 / 1,000,000 请求
    • Free tier:1,000,000 次/月 免费
  • Class B(读)

    • 单价:$0.36 / 1,000,000 请求
    • Free tier:10,000,000 次/月 免费

小项目前期:

  • 存储 < 10GB
  • 上传 < 1M 次/月
  • 访问 < 10M 次/月 → 基本都在免费档。

Infrequent Access(不常访问存储)

  • 存更便宜:$0.01 / GB-month

  • 但:

    • 读数据要收 Data Retrieval:$0.01 / GB
    • 有最少 30 天存储时间的约束
  • 你现在做图片站,访问比较频繁,先用 Standard 即可。


Class A / Class B 各包含哪些操作?

官方的分类:

  • Class A Operations

    • ListBuckets, PutBucket, ListObjects, PutObject, CopyObject
    • CompleteMultipartUpload, CreateMultipartUpload
    • LifecycleStorageTierTransition, ListMultipartUploads, UploadPart, UploadPartCopy, ListParts
    • PutBucketEncryption, PutBucketCors, PutBucketLifecycleConfiguration …
  • Class B Operations

    • HeadBucket, HeadObject, GetObject, UsageSummary
    • GetBucketEncryption, GetBucketLocation, GetBucketCors, GetBucketLifecycleConfiguration …
  • Free Operations

    • DeleteObject, DeleteBucket, AbortMultipartUpload

这些操作和 Cloudflare Worker 里的方法怎么对应

Worker 里你看到的是 JS API 封装名,但底层计费仍然按上面的 S3 风格名字算。

在 Worker 中绑定 bucket 后(例如 env.FILES_BUCKET_MAIN),常用方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
bucket.put(key, value, options?)          // → PutObject → Class A
bucket.get(key, options?)                // → GetObject → Class B
bucket.head(key)                         // → HeadObject → Class B
bucket.list(options?)                    // → ListObjects → Class A
bucket.delete(key | string[])            // → DeleteObject → Free

bucket.createMultipartUpload(key, ...)   // → CreateMultipartUpload → Class A
bucket.resumeMultipartUpload(key, id)    // 拿 R2MultipartUpload 对象

upload.uploadPart(partNumber, chunk, ...)  // → UploadPart → Class A
upload.complete(parts)                     // → CompleteMultipartUpload → Class A
upload.abort()                             // → AbortMultipartUpload → Free

Bucket 级别的操作(PutBucket、GetBucketCors 等)一般不在 Worker 里用,是给控制台 / REST API / CLI 用的。


常见问题

为什么删了文件,URL 还能访问?

有三层东西在一起工作:

  1. R2 存储本身

    • 真正的对象是否存在,取决于 head/getlist 的结果。
  2. Cloudflare CDN 缓存 (你用自定义域名 https://s.xxx.com/... 访问时)

    • 访问文件会被缓存;
    • 404 也会被缓存,所以“之前是 404 即使后来上传了同名文件,也可能继续 404”。
  3. 控制台 UI(Dashboard)

    • 文件列表是通过 ListObjects API 拿的,但 UI 不一定每次操作都即时刷新,可能看到“删除成功”但列表还是旧状态。

典型现象和解释

  • 现象 1:控制台提示 was successfully deleted,但列表里还在

    • 可能是:UI 还没刷新 / 有点小 bug。
    • 做法:刷新页面,重新 Search;或者用 head / CLI 确认真实状态。
  • 现象 2:控制台里列表没了,但 URL 还能访问

    • R2 里已经删掉,Cloudflare 还在用旧缓存。

    • 做法:

      • 访问 ...?test=123 这种新 query,如果变 404 → 说明是缓存;
      • 去 Cloudflare → Caching → Purge Cache by URL 清缓存。
  • 现象 3:URL 加参数是 404,但控制台 list 还能搜出来

    • 可能是:

      • URL 返回的 404 也是旧缓存(404 被缓存住了);
      • 或者删除和 list 之间存在短暂不一致 / UI 刷新问题。
    • 最权威的判断方法:

      • 用 Worker 里 bucket.head(key) 或 aws-cli aws s3 ls ... 看是否存在。

最终以什么为准?

最靠谱的是:

  • R2 的 head/get/list(不走 CDN)
  • 你的数据库记录 控制台和 URL 都可能有“缓存/延迟”,只能当辅助参考。