侧边栏壁纸
博主头像
一笑痕

人生若只如初见,
是可喜亦或者是可悲?

  • 累计撰写 253 篇文章
  • 累计收到 7 条评论

Range 的结束位置也包含在返回区间里

2026-10-28 / 0 评论 / 2 阅读

Range 的结束位置也包含在返回区间里

Range: bytes=2-5 要取几个字节?如果把结束位置当成不包含,服务端会给出 CDE;正确响应应是四个字节 CDEF。视频拖动时的分段请求依赖这条边界,断点续传也一样。状态码写了 206 还不够,区间、正文长度和 Content-Range 必须彼此对应。

先看三种容易写错的范围

字节位置从 0 开始。bytes=2-5 表示取第 2、3、4、5 号字节,区间两端都包含;对一个 10 字节文件来说,结束位置 9 是最后一个字节。请求 bytes=-3 表示从末尾倒数取 3 个字节,负号不代表起始位置。若起点已经达到文件长度,比如 bytes=10-,就没有可返回的字节,服务端应以 416 拒绝,并用 Content-Range: bytes */10 告知当前资源长度。

这些值要结合资源长度解释。起点、终点均是字节偏移,不是字符索引;文本若采用 UTF-8,多字节字符可能跨越偏移边界。按字符切片再把结果称作字节范围,会让正文和响应头不一致。

用本机服务逐项核对

本机用 Node.js 起了一个只监听回环地址的 HTTP 服务,资源正文固定为 ABCDEFGHIJ。脚本处理单段闭区间和后缀范围,再以系统分配的端口通过 Node 内置 fetch 请求它;没有经过 CDN、反向代理或外部网络。关键检查同时比较状态、Content-Range 和正文,不单独凭状态码判断成功。

原始输出保存在 tools/verify-fullstack-byte-range-20261028.log,脚本为 tools/verify-fullstack-byte-range-20261028.mjs。本机运行结果如下:

runtime: v26.8.1
resource: ABCDEFGHIJ (10 bytes)
Range bytes=2-5 -> 206; Content-Range=bytes 2-5/10; body=CDEF
Range bytes=-3 -> 206; Content-Range=bytes 7-9/10; body=HIJ
Range bytes=10- -> 416; Content-Range=bytes */10; body=(empty)
assertion: PASS (206 bounds, suffix range, and 416 response verified)

bytes=2-5 实际得到 CDEF,响应区间是 2-5/10;长度为 4,正好是 5 - 2 + 1。后缀范围命中 HIJ,对应 7-9/10。起点 10 没有对应字节,返回 416 和 bytes */10,正文为空。这三项断言均由脚本检查,不是从日志外推的描述。

服务端计算正文长度时可以直接按包含端点的公式处理:end - start + 1。取切片时却常用半开区间,因此要把结束位置加一,例如 buffer.subarray(start, end + 1)。少了这一步,响应头可能说返回四字节,实际正文却只给三字节。这样的错配在小文本里不显眼,放到文件分片合并时会变成缺字节或重叠数据。

416 不是「空文件」的意思

可满足范围的部分响应使用 206,并携带实际选中的 Content-Range,其总长度也要准确。资源长度是 10 时,bytes=10- 超出范围;bytes */10 中的星号代表没有可返回的区间,斜线后面的 10 仍告诉客户端资源总长。客户端可以据此决定是否重试完整下载或刷新元数据。

结束位置超过末尾时,服务端可以把它截到最后一个字节。例如 10 字节资源上的 bytes=8-20,可满足部分是 8 到 9。脚本这次没有覆盖该分支,也没测试无效语法、多段 Range 或 If-Range;这些实现细节不能从三条成功日志里推出。实际服务若接收任意范围,还应设定分段数量和总工作量上限,避免解析器被异常输入拖入过量内存或 CPU 消耗。

一个特别容易混淆的写法是 bytes=-0。它不能表示「整个文件」或最后一段;零长度后缀不产生可满足的字节范围。是否将它归为语法无效还是不可满足范围,应按所采用的协议实现与测试边界处理,不能静默改成整份资源。多段范围需要 multipart/byteranges 格式,本次脚本没有实现这种情况;把多个区间直接拼成一个正文会丢掉区间边界。

若文件在生成分段之间发生变化,单靠 Range 会把不同版本的片段拼在一起。断点续传应配合实体标签等验证器,通过 If-Range 确认资源仍是同一版本;不匹配时返回完整表示,而非继续交付旧偏移片段。这里没有测验证器流程,我不把固定内存缓冲区上的结果当成续传方案已经安全的证据。

静态资源服务器若已经由框架或代理正确实现 Range,通常不必重复造一个解析器;自定义下载接口则要核实返回区间与正文长度是否一致,也要确认总长度和端点都准确。小心地把 206 当成「任意部分内容成功」会漏掉最关键的边界。只要返回头声称的区间与字节数不能一一对应,这个响应就不可信。

    🤞 分享