NO QUERY只能按前缀翻页
list 没有 WHERE、没有 JOIN、没有排序。想问「这个客户的文件有几份」,只能问数据库。
对象存储只管字节。谁拥有它、还在不在、能不能删——全靠库里那一行。
// 面向要接 R2 的工程师 · 实测口径 2026-08
// 本文为公开版,具体标识已替换为占位符
R2 只做一件事:用一个 key 存取一整份字节。没有目录、不能改一半、也不能 WHERE。
key → 整份字节
写入即整份覆盖,没有随机写。扁平命名空间,「目录」只是 key 前缀。图片、视频、备份、安装包的归宿。
路径 → 可随机读写
能 seek、能改一个字节、有真目录树。代价是要挂载、要扩容、冗余得自己管。
行 → 索引 + 事务
适合查、改、关联、约束。不适合塞几 MB 的二进制——那会把备份和连接池一起拖垮。
货放对象存储,目录留数据库。R2 里的每个 key 都必须在库里有一行认领它——没有那一行,这份字节就既查不到、也没人敢删。
出口流量免费。存得多不便宜,发得多才便宜——媒体分发场景差距是数量级的。
// R2 侧 $0.015 / GB·月 + 出站 $0(官网复核 08-18)· S3 侧为参考挂牌价,未复核
list 没有 WHERE、没有 JOIN、没有排序。想问「这个客户的文件有几份」,只能问数据库。
默认 R2 token 没有 GraphQL Analytics 权限,Class A/B 操作量读不到——实测拿不到桶热度。
「指定管辖权地」只有 EU / FedRAMP。要亚太得选自动 → 位置提示 → APAC。
新桶自带一条 7 天 multipart abort。同前缀再加一条同类规则,行为未定义——改那条就好。
一个账户通常按业务拆成 N 个桶。先问每个桶「谁能从公网直接读到它」——答案只有三种,而最省事的那种最难收回。
裸桶直读、零配置。URL 一旦被写进代码或数据,就等于关不掉——收口即断链。
挂进自己的 zone,才有缓存规则、WAF 与鉴权可用。对客分发的规范出口。
没有任何公开面,只经服务端转发访问。备份与内部资料的唯一归宿。
老桶最常见的缺口是没有过期类生命周期规则。停写的桶、预览图、构建残留会一直躺着——没人认领,也没人敢删。建桶当天就把生命周期和「谁认领」一起定下来。
我们见过最干净的范本是一张资产表:读、写、删全过数据库,R2 只是从属副本。
// 反过来「先传后记」会在任何失败点留下没人认领的孤儿对象
// B=存了完整 URL 而非 key · C=索引在本机 SQLite · D=域名硬编码进代码
库里存 https://pub-xxx.r2.dev/a.jpg 的那一刻,换域名就得刷全表;存 storage_key 则只需改一处拼接。这就是 A 档与 B 档的全部差别。
给谁看,决定走哪条路。选错就是数据裸奔,或者国内根本打不开。
零配置直连公网,适合临时验证。一旦写进代码就再也关不掉——它还会把桶的存在一起暴露出去。
挂进 zone 才有缓存规则与 WAF。缓存规则的主机名必须用 eq,wildcard 实测从不命中。
浏览器只碰自家域名,服务端取字节直接把 Response.body 接下去。别 arrayBuffer——512MB 机器会 OOM。
签名 URL 不是万能:指向 *.supabase.co 的签名地址在国内解析不了——签名有效,只是那台机器到不了。凭据还得放路径段,因为 <img> / <a download> 带不了自定义头。
现行规则 edge 1 月 / browser 1 年,排除备份前缀。验证命中必须用 GET——HEAD 会误报 DYNAMIC。
200 ≠ 内容对,Content-Type 对 ≠ 文件全。HEAD 状态 + 魔数 + 线上引用三步缺一不可。
全部是实测记录,不是理论风险。最贵的一个整整绿灯了 15 天。
pg_dump | gzip 没开 pipefail,取的是 gzip 的退出码;客户端 16.14 撞服务端 17.10 直接 abort。产物 20 字节,CI 全绿。
桶一旦挂了公开域名,里面任何前缀都可能被公网直接读到——敏感产物永远不进带公开域名的桶。
不是沙箱也不是网络:ID 错了 SNI 就非法。另外,模板 env 文件里的 R2 变量常常只是占位符,别信。
自定义域没有根对象。分发要给完整对象路径,而且必须可点击——截图里的 56 字符 URL 没人能手输对。
主机名用 wildcard 写的缓存规则一直没生效,全量回源还看不出来。改成 eq 后立刻 MISS → HIT。
代码里写的是 pub-xxxxxxxx 前缀,不是桶名。要搜 hash——按桶名 grep 会得出「没人用,可以关」的假结论。
上面每一个坑,收口动作都很具体。建桶、写第一行上传代码之前对一遍。
gzip -t 与体积阈值校验storage_key 不存 URL「退出码 0」不等于「业务有效」。任何写入 R2 的定时任务都要落两个指标:脚本自身心跳,以及产物体积/条数的 delta——只看绿灯,等于没监控。
| 项 | 做法 | 口径 / 备注 |
|---|---|---|
| 端点 | https://<accountId>.r2.cloudflarestorage.com | S3 兼容 |
| 建桶位置 | 自动 → 位置提示 → 亚太 APAC | 管辖权地只有 EU |
| 生命周期 | 编辑默认那条 multipart abort | 别新增同前缀 |
| CORS | JSON 压成单行再粘贴 | CodeMirror 会污染 |
| 缓存规则 | http.host eq "media.example.com" | wildcard 不命中 |
| 验证缓存 | 用 GET 看 cf-cache-status | HEAD 误报 DYNAMIC |
| 落库范式 | 建行 → presign → 直传 → 回写 | 存 key 不存 URL |
| 私有原件 | 服务端流式转发,凭据放路径段 | 不发签名 URL |
| 失败语义 | 查不到 / 不是你的 / 路径非法 一律 404 | 403 = 探测接口 |
| 备份产物 | pipefail + gzip -t + 体积阈值 | 私桶,零公开域 |
| 上传验证 | HEAD 200 + 魔数 + 线上引用 | 三步缺一不可 |