对象存储接口规范 0.2.0
本包让应用对已授权的对象桶读取元数据、读取文字与字节、写入(可带条件与摘要校验)、删除、列举,并把对象正文流直接作为当前入站请求的最终响应。它是云工专用能力,当前映射到 Cloudflare R2 绑定。桶名是应用传入的绑定名,由宿主的资源许可核对(例如 {"R2":["PACKAGES"]}),规范不硬编码具体绑定名。结构化数据用 JSON 文字;可恢复的业务情形用返回值表示(对象不存在返回 null,条件写不满足返回 {"written":false},直通对象不存在返回阴),违规、超限和平台故障抛出豫言异常,应用可用「尝试运行」捕获,平台故障的异常消息保留平台报告的原文(如 R2 的错误码文字)。桶名不在许可内、许可内的桶未绑定,属于部署配置错误,宿主直接中止当前请求,应用不应尝试恢复。
桶名与键
- 桶名:1 至 64 字节,首字节为字母或下划线,其余为字母、数字或下划线;不符抛出异常。是否获授权另由宿主许可核对。
- 键:1 至 1024 字节(UTF-8),不含控制字符(U+0000 至 U+001F 与 U+007F,含 NUL),不以斜杠
/开头;不符抛出异常且不调用平台。这比 R2 自身的规则更严:R2 接受控制字符与首斜杠,本包宁严勿松,使日志、标头与 JSON 不受控制字符牵连。列举结果里的键不受此限,按对象实际的键原样返回。
元数据
对象的元数据用 JSON 文字表示,字段顺序固定:{"size":字节数,"etag":"…","httpEtag":"\"…\"","uploaded":上传时刻的 Unix 毫秒整数,"httpMetadata":{…},"customMetadata":{…}}。etag 是不带引号的校验值,httpEtag 是带双引号的形态(可直接用于 ETag、If-Match);httpMetadata 只含存在的字段:contentType、contentLanguage、contentDisposition、contentEncoding、cacheControl(字符串)以及 cacheExpiry(Unix 毫秒整数);customMetadata 是字符串到字符串的 JSON 对象,没有时为 {}。
取对象元数据
取对象元数据 桶 键 只读元数据,不读取正文。对象不存在返回字面量 null。
读取对象文字
读取对象文字 桶 键 字节上限 把对象正文读成字符串。字节上限须在 1 至 2097152(2 MiB) 之间,越界抛出异常。
- 不存在:字面量
null。 - 对象字节数大于字节上限:不读取正文、不截断,返回
{"tooLarge":true,"size":字节数}。 - 否则用严格的 UTF-8 解码(非法序列、截断的多字节字符、过长编码、代理项编码都抛出异常),返回
{"text":"…","size":字节数,"httpEtag":"…","customMetadata":{…}}。开头的 BOM(U+FEFF)原样保留在text里,size是原始字节数。空对象得"text":""。 - 为什么上限是 2 MiB 而不是宿主值桥的 16 MiB:正文经宿主转成 JSON 字符串字面量后越过值桥,控制字符在 JSON 里最坏膨胀为六倍(
\u0001),2 MiB 的最坏情形约 12 MiB,仍在 16 MiB 之内;更大的文字对象应改用流式直通,或由应用自己分块管理。
读取对象字节
读取对象字节 桶 键 字节上限 把对象正文读成字节串,返回(爻,字节串)二元组。字节上限须在 1 至 16777216(16 MiB) 之间,越界抛出异常。
- 不存在:(阴,空字节串),不抛异常。
- 存在且对象字节数不大于字节上限:(阳,全部正文字节)。空对象得(阳,空字节串)。
- 对象字节数大于字节上限:不读取正文,抛出异常(不同于
读取对象文字返回tooLarge:字节读取的调用者通常已经知道大小,需要区分时先用取对象元数据查size)。 - 字节经宿主值桥以二进制直接传递,不经 JSON 转义,因此上限可以到值桥的 16 MiB;正文不做任何解码,也不做 UTF-8 检查(要严格文字请用
读取对象文字)。 - 用途:需要在内存里改写较大的对象(例如给已渲染的 HTML 页面补链接)而 2 MiB 的文字上限不够时使用;只是把对象交给客户端时仍应使用流式直通,不要读进内存。
写入对象与写入对象文字
写入对象 桶 键 选项JSON 正文(正文为字节串)与 写入对象文字 桶 键 选项JSON 正文(正文为字符串,按其 UTF-8 字节写入)写入一个对象,覆盖同名对象。正文不得超过 16 MiB。
选项 JSON 是对象,所有字段都可缺省;空字符串、null 和 {} 都表示无选项;出现未知字段、重复字段、类型不符都抛出异常:
| 字段 | 类型与规则 | 含义 |
| --- | --- | --- |
| ifNoneMatch | 只能是 "*" | 仅当对象不存在时写入 |
| ifMatch | 带双引号的 etag,双引号内 1 至 64 个字母、数字或连字符(形如 httpEtag) | 仅当对象存在且 etag 相符时写入 |
| sha256 | 64 个小写十六进制字符 | 由平台校验正文的 SHA-256 |
| contentType | 1 至 256 字节的可见 ASCII | 保存为 httpMetadata.contentType |
| cacheControl | 1 至 256 字节的可见 ASCII | 保存为 httpMetadata.cacheControl |
| customMetadata | 字符串到字符串的对象:至多 64 项;键 1 至 128 字节;值至多 1024 字节;键值都不含控制字符;键不得是 $句柄、$未定义、$大整数、$数字(宿主桥的保留名,会被当作句柄引用) | 自定义元数据;平台另有总大小限制,超出由平台报错 |
ifNoneMatch 与 ifMatch 不能同时给出。条件写用真正的 Headers 传给平台(R2 会静默忽略普通对象形式的条件,所以适配不用它)。
返回 JSON:成功 {"written":true,"size":n,"etag":"…","httpEtag":"…"};前置条件不满足(ifNoneMatch 而对象已存在,ifMatch 而 etag 不符或对象不存在)为 {"written":false},对象保持原样,不抛异常。sha256 不符、正文超限、选项违规、平台故障抛出异常,摘要不符的异常消息保留平台原文(含 The SHA-256 checksum you specified did not match what we received 与错误码 10037),此时不留下对象。条件与摘要同时给出时,先判哪一个由平台决定(本地 workerd 的 R2 先校验摘要:摘要不符即使条件也不满足仍抛出异常),应用不应依赖这一顺序。
删除对象
删除对象 桶 键们(键们是字符串列表)删除至多 1000 个对象;不存在的键不报错;空列表不调用平台;列表超过 1000 项或任一键不合规,整批抛出异常,且不调用平台(不会删除一部分)。
列举对象
列举对象 桶 选项JSON 列举对象,选项字段都可缺省(空字符串、null、{} 表示无选项):prefix(字符串,0 至 1024 字节)、limit(整数 1 至 1000,缺省 1000)、cursor(上次返回的续标,1 至 4096 字节)、delimiter(1 至 16 字节)、include(数组,只接受 ["customMetadata"]);前缀、续标、分隔符都不得含控制字符;未知或重复字段、类型不符抛出异常。
返回 {"objects":[{"key":"…","size":n,"etag":"…","uploaded":毫秒,"customMetadata":{…}}],"truncated":真假,"cursor":"…"或null,"delimitedPrefixes":[…]}:customMetadata 仅在 include 含 customMetadata 时出现;truncated 为真时 cursor 是续标,否则为 null;给了 delimiter 时,含分隔符的键折叠进 delimitedPrefixes。结果按键的 UTF-8 字节序排列。给了 `include` 时,平台可能每页返回少于 `limit` 项(R2 文档:设置了 include 可能得到少于 limit 的结果),应用必须以 truncated 与 cursor 续列,不能用返回数是否等于 limit 判断是否还有下一页。
回应入站以对象存储对象
回应入站以对象存储对象 桶 键 状态 附加标头 把对象正文流式直通为当前入站请求的最终响应,正文不进入豫言内存(因此不受 16 MiB 的值桥限制,可以是任意大小)。返回爻:对象存在并已设置最终响应为阳;对象不存在为阴,此时不占用最终响应,应用可以接着回 404。
- 响应自动带
Content-Length(对象字节数)与ETag(httpEtag),再叠加附加标头。 - 当前请求方法为
HEAD时只读元数据(用head),响应无正文但带Content-Length与ETag;其他方法读取正文流。不支持范围请求:始终交付整个对象。 - 状态须在 200 至 299 之间,且不能是无正文的 204、205,也不能是须按范围交付的 206;否则抛出异常。
- 附加标头是(名, 值)对列表,至多 64 项:名须是 HTTP token(字母、数字及
!#$%&'*+-.^_|~),1 至 128 字节;值须是可见 ASCII(0x20 至 0x7E),至多 4096 字节,首尾不得为空格;名不区分大小写,不得重复;下列名由平台或本函数设定,或涉及连接与帧,不得给出:Content-Length、Transfer-Encoding、Connection、Keep-Alive、Upgrade、TE、Trailer、Proxy-Authenticate、Proxy-Authorization、Proxy-Connection、Set-Cookie、ETag`。违规抛出异常。 - 无当前入站请求(如队列或定时事件)、本次请求已经有最终响应(含重复调用)属于调用错误,宿主中止本次运行。一次请求只有一个最终响应。
- 平台行为提示:对可压缩的内容类型(如
text/*),当客户端接受压缩时,平台可能自动压缩并改用分块传输,此时响应没有Content-Length,ETag也可能被弱化;需要严格长度和强校验的下载应使用不可压缩的类型(如application/octet-stream)。
限额一览
| 项目 | 限额 | 违规时 |
| --- | --- | --- |
| 桶名 | 1 至 64 字节的绑定名 | 豫言异常 |
| 键 | 1 至 1024 字节,无控制字符,不以斜杠开头 | 豫言异常 |
| 写入正文 | 16 MiB | 豫言异常 |
| 读取对象文字的字节上限 | 1 至 2 MiB | 豫言异常;对象更大时返回 tooLarge |
| 读取对象字节的字节上限 | 1 至 16 MiB | 豫言异常;对象更大时也抛出豫言异常 |
| 选项 JSON | 64 KiB | 豫言异常 |
| 删除 | 1000 个键 | 豫言异常 |
| 列举 | limit 1 至 1000 | 豫言异常 |
| 直通附加标头 | 64 项,值 4096 字节 | 豫言异常 |
跨宿主一致性
任何实现须满足:对象不存在时 取对象元数据 与 读取对象文字 返回 null,直通返回阴且不占用最终响应;条件写不满足返回 {"written":false} 且对象不变;摘要不符不留下对象;tooLarge 不读取正文;直通不把正文复制进应用内存;uploaded 是 Unix 毫秒整数;列举的续标是不透明字符串,只由上一次返回的 cursor 得到。R2 之外的实现(例如文件系统或别的对象存储)也须遵守以上语义与限额。
一致性样例
- 写入
abc且{"ifNoneMatch":"*"}:对象不存在得{"written":true,"size":3,"etag":"900150983cd24fb0d6963f7d28e17f72","httpEtag":"\"900150983cd24fb0d6963f7d28e17f72\""},再写一次得{"written":false}。 - 写入
abd且sha256为abc的摘要:抛出异常,消息含did not match,取对象元数据得null。 - 写入 1000 字节后
读取对象文字 … 999得{"tooLarge":true,"size":1000},上限为 1000 时得全文。 - 写入
00 ff 0a(含 NUL 与非 UTF-8 字节)后读取对象字节 … 16777216得(阳,同样三个字节);不存在的键得(阴,空);上限为 2 而对象有 3 字节抛出异常;上限为 0 或 16777217 抛出异常。 - 键
/a、空键、含换行的键、1025 字节的键:抛出异常;1024 字节的键可用。 - 列举 7 个对象,
limit3:三页(3、3、1),前两页truncated为真,末页为假且cursor为null。 - 直通
GET:响应体等于对象字节,Content-Length为对象字节数,ETag为httpEtag;HEAD:无体、头相同;对象不存在:阴,应用另回 404。
边界
本包不做:分片上传、范围读取、按条件读取、复制或移动对象、改写元数据而不重写正文、预签名地址、对象生命周期与桶管理;字节读取(读取对象字节)至多 16 MiB,更大的对象只能用直通;不解释对象内容的格式。存储类别、加密密钥等 R2 的高级选项未开放。