范围请求
Range 的三种范围形式、Content-Range 语法、206 与 multipart/byteranges 的构造。
作者 David更新于 第 16 篇(共 22 篇)
Accept-Ranges
Accept-Ranges = acceptable-ranges
acceptable-ranges = 1#range-unit / "none"响应字段,声明服务端是否支持范围请求:
Accept-Ranges: bytes
Accept-Ranges: none唯一标准单位是 bytes。none 表示明确不支持。字段缺失时客户端不应假定支持,但可以试探性发送 Range。
Range
Range = ranges-specifier
ranges-specifier = range-unit "=" range-set
range-set = 1#range-spec
range-spec = int-range / suffix-range / other-range
int-range = first-pos "-" [ last-pos ]
suffix-range = "-" suffix-length三种形式
设表示总长为 10000 字节:
| 写法 | 含义 | 实际字节 |
|---|---|---|
bytes=0-499 | 第 0 到第 499 字节 | 前 500 字节 |
bytes=500- | 从第 500 字节到末尾 | 后 9500 字节 |
bytes=-500 | 最后 500 字节 | 第 9500–9999 字节 |
两端均为闭区间——bytes=0-499 是 500 字节,不是 499。这是最高频的差一错误。
bytes=-500 的前导 - 不是负数,是「后缀长度」记法。
多范围
Range: bytes=0-499, 1000-1499, -200- 范围可以重叠、可以乱序。
- 服务端 MAY 合并重叠范围、MAY 按升序重排、MAY 只返回其中一部分。
- 服务端 MAY 因范围过多(DoS 防护)直接返回
200完整内容——这是允许的降级。
服务端处理规则
Range仅对GET有定义;其他方法收到时 MUST 忽略。first-pos>last-pos→ 该 range-spec 非法,整个Range字段 MUST 被忽略(按200处理)。- 所有范围都超出表示长度 → 416 Range Not Satisfiable。
- 部分范围可满足 → 只返回可满足的那些,
206。 last-pos超出末尾 → 截断到末尾,不算错误。- 服务端始终可以忽略
Range返回完整200。
Content-Range
Content-Range = range-unit SP ( range-resp / unsatisfied-range )
range-resp = incl-range "/" ( complete-length / "*" )
incl-range = first-pos "-" last-pos
unsatisfied-range = "*/" complete-length三种形态:
Content-Range: bytes 0-499/10000 表示总长已知
Content-Range: bytes 0-499/* 总长未知(流式生成)
Content-Range: bytes */10000 416 响应,告知实际总长要点:
- 单位与范围之间是 SP,不是
=(与Range相反)——高频写错点。 complete-length是完整表示的长度,不是本次返回的长度;本次长度由Content-Length给出。Content-Range在PUT请求中无意义,服务端收到 MUST 返回400。
206 的构造
单范围
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-499/10000
Content-Length: 500
Content-Type: text/html
<500 字节数据>- MUST 带
Content-Range。 Content-Length= 本次返回的字节数(500),不是 10000。
多范围:multipart/byteranges
HTTP/1.1 206 Partial Content
Content-Type: multipart/byteranges; boundary=SEP
--SEP
Content-Type: text/html
Content-Range: bytes 0-499/10000
<数据>
--SEP
Content-Type: text/html
Content-Range: bytes 1000-1499/10000
<数据>
--SEP--规则:
- 不带顶层
Content-Range——每个 part 各自带。 - 每个 part MUST 带
Content-Range,SHOULD 带Content-Type。 - 即使只请求了一个范围,服务端也可以用 multipart 形式回应;客户端必须能处理两种形态。
206 中的其他字段
206 MUST 包含若返回 200 时会发的这些字段:Date、Cache-Control、ETag、Content-Location、Expires、Vary。
If-Range
If-Range = entity-tag / HTTP-date解决断点续传时资源已变的问题。客户端把上次拿到的验证器带上:
GET /big.iso HTTP/1.1
Range: bytes=5000000-
If-Range: "abc123"服务端处理:
- 验证器匹配 → 资源未变,返回
206续传部分。 - 验证器不匹配 → 资源已变,忽略
Range,返回完整200(而不是412)。
关键约束:
- 必须用强验证器。用
ETag时是强比较;弱 ETag 不可用于If-Range。 - 用日期形式时,仅当服务端能确保该
Last-Modified是强验证器(与Date差 ≥1 秒)才可信任。 If-Range无Range时必须被忽略。- 该设计让客户端一次往返就能处理两种情况:要么续传,要么重下。
缓存与范围
- 缓存可以存储
206的部分内容,并在后续范围请求时拼接已存片段——前提是各片段的验证器相同。 - 验证器不同的片段MUST NOT 拼接(会产生损坏文件)。
- 这正是弱 ETag 被禁止用于
Range的根本原因。
下一篇:缓存——Cache-Control 指令全集与新鲜度计算。