【问题标题】:HTTP Range headerHTTP 范围标头
【发布时间】:2011-03-19 04:44:16
【问题描述】:

我正在阅读http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35 并试图弄清楚如何继续下载文件。

例如,假设一个文件的长度为 100 个字节,而我拥有全部 100 个字节。但是,我不知道预期的文件大小应该是多少,所以我要求该文件并指定一个 Range 标头,如下所示:

Range: bytes=100-

这是一个有效的 Range 请求吗?

【问题讨论】:

  • Erm,它下面的示例引用'bytes = 9500-'作为有效,所以....
  • 最新的 ref 是 RFC7233 -- httpwg.github.io/specs/rfc7233.html
  • 您可以先发出 HEAD 请求并检查文件长度。

标签: http http-headers header


【解决方案1】:

这是一个语法上有效的请求,但不是一个可满足的请求。如果您进一步查看该部分,您会看到:

如果一个语法上有效的字节范围集包括至少一个字节范围规范,其第一个字节位置小于实体主体的当前长度,或者至少一个后缀字节范围规范具有非零后缀长度,则字节范围集是可满足的。否则,字节范围集是不可满足的。 如果字节范围集不可满足,服务器应返回状态为 416(请求范围不可满足)的响应。否则,服务器应该返回一个状态为 206(部分内容)的响应,其中包含实体主体的可满足范围。

所以我认为在您的示例中,服务器应该返回 416,因为它不是该文件的有效字节范围。

【讨论】:

  • 那么有什么方法可以让客户端在不进行 HEAD 调用以首先计算内容长度然后进行数学运算并获取实际内容的情况下恢复下载?我的意思是某种开放式寻址,例如“在某个字节之后给我所有字节......”
  • 客户端已经知道它是否拥有来自原始请求的所有数据 - 它应该在原始响应中接收到 Content-Length 标头,或者如果它被分块编码,它将收到一个长度为零的块,表示响应已完成。如果您没有保存此状态并且磁盘上只有一大块字节,那么是的,您必须执行 HEAD 请求或使用 Range 标头请求字节范围,如果您返回 416回复你知道你有所有的字节。
  • 我认为 Expect-Continue 可以让您或多或少地按照需要进行流式传输块?
  • @MarcNovakowski 实际上,考虑 wget 的情况并使用 -c 标志。由于 wget 不维护有关文件完整的任何元数据,因此假设磁盘上文件的大小为 99 字节。 wget 将请求字节范围“100-”,我觉得服务器应该以 0 长度响应响应,因为请求仅在文件末尾之后 1。
【解决方案2】:

正如Wrikken 建议的那样,这是一个有效的请求。当客户端请求媒体或恢复下载时,这也很常见。

客户端通常会测试服务器是否处理范围请求,而不仅仅是寻找Accept-Ranges 响应。 Chrome总是会发送Range: bytes=0- 与它对视频的第一个 GET 请求,所以你不能忽略它。

每当客户端在其请求中包含 Range: 时,即使它的格式不正确,它也会期待部分内容 (206) 响应。在 HTML5 视频播放期间向前搜索时,浏览器只请求起点。例如:

Range: bytes=3744-

因此,为了让客户端正确播放视频,您的服务器必须能够处理这些不完整的范围请求。

您可以通过两种方式处理您在问题中指定的“范围”类型:

首先,您可以使用响应中给出的请求起点进行回复,然后是文件的总长度减一(请求的字节范围为零索引)。例如:

请求:

GET /BigBuckBunny_320x180.mp4 
Range: bytes=100-

回复:

206 Partial Content
Content-Type: video/mp4
Content-Length: 64656927
Accept-Ranges: bytes
Content-Range: bytes 100-64656926/64656927

其次,您可以回复请求中给出的起点和开放式文件长度(大小)。这适用于总长度未知的网络广播或其他媒体。例如:

请求:

GET /BigBuckBunny_320x180.mp4
Range: bytes=100-

回复:

206 Partial Content
Content-Type: video/mp4
Content-Length: 64656927
Accept-Ranges: bytes
Content-Range: bytes 100-64656926/*

提示:

您必须始终使用范围中包含的内容长度进行响应。如果范围是完整的,从头到尾,那么内容长度就是差异:

请求: 范围:字节=500-1000

回应: 内容范围:字节 500-1000/123456

请记住,范围是从零开始的,所以Range: bytes=0-999 实际上请求的是 1000 个字节,而不是 999,因此请使用以下内容进行响应:

Content-Length: 1000
Content-Range: bytes 0-999/123456

或者:

Content-Length: 1000
Content-Range: bytes 0-999/*

但是,如果可能,请避免使用后一种方法,因为一些媒体播放器会尝试从文件大小中计算出持续时间。如果您的请求是针对媒体内容的,这是我的预感,那么您应该在响应中包含其持续时间。这是通过以下格式完成的:

X-Content-Duration: 63.23 

这必须是一个浮点数。与Content-Length 不同,此值不必准确。它用于帮助播放器在视频中搜索。如果您正在流式传输网络广播,并且只知道它会持续多长时间,那么最好包括您的估计持续时间,而不是完全忽略它。因此,对于两个小时的网络广播,您可以包括以下内容:

X-Content-Duration: 7200.00 

对于某些媒体类型,例如 webm,您还必须包含 content-type,例如:

Content-Type: video/webm 

所有这些都是媒体正常播放所必需的,尤其是在 HTML5 中。如果您不提供持续时间,播放器可能会尝试从文件大小中计算出持续时间(以允许搜索),但这并不准确。这很好,对于网络广播或实时流媒体来说是必要的,但对于视频文件的播放来说并不理想。您可以使用 FFMPEG 等软件提取持续时间并将其保存在数据库中,甚至保存在文件名中。

X-Content-Duration 正在逐步淘汰,取而代之的是Content-Duration,所以我也将其包括在内。对“0-”请求的基本响应至少包括以下内容:

HTTP/1.1 206 Partial Content
Date: Sun, 08 May 2013 06:37:54 GMT
Server: Apache/2.0.52 (Red Hat)
Accept-Ranges: bytes
Content-Length: 3980
Content-Range: bytes 0-3979/3980
Content-Type: video/webm
X-Content-Duration: 2054.53
Content-Duration: 2054.53

还有一点:Chrome 总是以以下方式开始其第一个视频请求:

Range: bytes=0-

某些服务器会发送常规的 200 响应作为回复,它会接受(但播放选项有限),但尝试发送 206 来代替显示您的服务器处理范围。 RFC 2616 表示忽略范围标题是可以接受的。

【讨论】:

  • 如果内容是没有固定时长的实时视频流,你会怎么做?
  • @Joel,即使您不知道,也需要回复持续时间。在这种情况下,只需尝试 0.0。对于客户而言,持续时间无论如何都无关紧要,因为您通常无法扫描实时流。如果 0.0 不起作用,请尝试非常高的值,例如 1000000.00。
  • @VictorStoddard 可以将分块流式传输应用于客户端请求中没有 Range 标头的常规文件下载吗?这种情况下服务器应该如何响应?
  • @gkiko 除了在分块传输编码中使用 Transfer-Encoding 标头而不是 Content-Length 之外,没有太大区别。块可以来自单个文件,服务器可以设置块大小。客户端应在收到块时缓冲并拼凑这些块。或者,HTTP 流使用媒体文件的预先录制的片段,它们作为单独的部分(ts 文件)保存在服务器上。这些段使用从索引文件获取的常规 HTTP 文件 GET 请求提供服务。我发现分段很棘手,但那是几年前的事了。
  • Content-Length: 64656927 Accept-Ranges: bytes Content-Range: bytes 100-64656926 为什么 Content-Length 不是 '64656827'?
【解决方案3】:

与 Mark Novakowski 的回答相反,由于某种原因得到了许多人的支持,是的,这是一个有效且可满足的要求。

事实上,正如 Wrikken 所指出的,标准就是这样一个例子。在实践中,Apache 会按预期响应此类请求(使用 206 代码),而这正是我用来实现渐进式下载的方式,即只获取随着轮询实时增长的长日志文件的尾部。

【讨论】:

  • 请再次阅读 Marc Novakowki 的回答。 “可满足”在他引用的 RFC 中具有特殊含义。此请求无法满足,因为请求的字节超出了文件的长度。
  • Firefox 不是响应请求的软件元素,它是一个 http 服务器
【解决方案4】:

对于那些在 2019 年偶然发现 Victor Stoddard 的上述答案并变得充满希望和目光敏锐的人们,请注意:

a) Firefox 41 中删除了对 X-Content-Duration 的支持:https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/41#HTTP

b) 我认为它仅在 Firefox 中支持 .ogg 音频和 .ogv 视频,不支持任何其他类型。

c) 我看不到 Chrome 完全支持它,但这可能只是我缺乏研究。但是,截至今天,Chrome 71 中的 webm 或 ogv 视频,它的存在与否似乎没有任何影响。

d) 我在任何地方都找不到 'Content-Duration' 替换 'X-Content-Duration' 的任何地方,我认为 'X-Content-Duration' 的寿命不够长,以至于没有后续标题名字。

我认为这意味着,从今天开始,如果您想将包含不知道其持续时间的流(例如 ffpeg 管道的输出)的流提供给 Chrome 或 FF,并且您希望它们在 HTML 5 视频元素中可擦洗,你可能不走运。无论您是否通过范围请求提供服务,Firefox 64.0 都会半心半意地尝试使这些内容可擦洗,但如果您搜索的次数比它认为合适的多几倍,它就会感到困惑并抛出一个旋转的轮子,直到流完全下载。 Chrome 甚至没有尝试,它只是不让你擦洗,直到整个流完成播放

【讨论】:

【解决方案5】:

如果您尝试请求长度未知的内容,并且希望它返回连续(或聚合)响应,那么您可能需要考虑使用RFC8673 中建议的方法 - 即设置last-byte-pos 到 2^^53-1 所以你的请求看起来像这样:

GET /resource HTTP/1.1
Host: example.com
Range: bytes=0-9007199254740991

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 2013-09-04
    • 1970-01-01
    • 1970-01-01
    • 2013-03-27
    • 2011-07-17
    相关资源
    最近更新 更多