HTTP 如何处理大文件的传输--range Content-Range解释

对于几百 M 甚至上 G 的大文件来说,如果要一口气全部传输过来显然是不现实的,会有大量的等待时间,严重影响用户体验。因此,HTTP 针对这一场景,采取了范围请求的解决方案,允许客户端仅仅请求一个资源的一部分。

对于相对较小的大文件传输,解决办法还有:数据压缩、分块传输

1.数据压缩

浏览器在发送请求时都会带着 Accept-Encoding 头字段,里面是浏览器支持的压缩格式列表,例如 gzip、deflate、br 等,这样服务器就可以从中选择一种压缩算法,放进 Content-Encoding 响应头里,再把原数据压缩后发给浏览器。

2.分块传输

除了压缩文件之外,另一种办法就是分块传输。它们的原理差不多,都是把大文件变小传输。分块传输会把一个大文件切成很多小块,把这些小块依次发给浏览器,浏览器收到之后再组装复原。这样浏览器和服务器都不用在内存中保存全部文件,每次只收发一小部分,网络也不会被大文件长时间占用,内存、带宽等资源也就节省下来了。

具体实现是在 response 响应报文里用头字段 Transfer-Encoding: chunked 来表示,表示报文里的 body 部分不是一次性发过来的,而是分成了许多的块(chunk)逐个发送。当 chunk 为 0 时说明是最后一个,传输结束。

Transfer-Encoding 和 Content-Length 两个字段是互斥的,不能同时出现。一个响应报文的长度要么是已知的,要么是未知的。

3.范围请求

你看电影时,想跳过开头直接看正片,这实际上是想获取一个大文件其中的片段数据,而分块传输没有这个能力。或者请求更大文件。

HTTP 协议为了满足这种需求,提出了范围请求的概念,允许客户端在请求头里使用专用字段来表示只获取文件的一部分。

如何支持:
范围请求不是 Web 服务器必须实现的功能,所以服务器必须在响应头里使用字段

Accept-Ranges: bytes 

明确告知客户端自己支持范围请求。如果不支持的话,服务器就会发送

Accept-Ranges:none

Range 字段拆解
而对于客户端而言,它需要指定请求哪一部分,通过Range这个请求头字段确定,格式为bytes=x-y。接下来就来讨论一下这个 Range 的书写格式:

  • 0-499表示从开始到第 499 个字节。
  • 500- 表示从第 500 字节到文件终点。
  • -100表示文件的最后100个字节。

服务器收到 Range 字段后,需要做四件事。
第一,它必须检查范围是否合法,比如文件只有 100 个字节,但请求“200-300”,这就是范围越界了。服务器就会返回状态码 416 Requested Range Not Satisfiable,表示范围请求有误,无法处理。

第二,如果范围正确,服务器就可以根据 Range 头计算偏移量,读取文件的片段了,返回状态码 206 Partial Content,表示 body 只是原数据的一部分。

第三,服务器要添加一个响应头字段 Content-Range,告诉片段的实际偏移量和资源的总大小,格式是 「bytes x-y/length」,与 Range 头区别在没有“=”,范围后多了总长度。例如,对于“0-10”的范围请求,值就是“bytes 0-10/100”。

最后剩下的就是发送数据了,直接把片段用 TCP 发给客户端,一个范围请求就算是处理完了。

具体来说,请求单段数据和请求多段数据,响应头是不一样的。

// 单段数据
Range: bytes=0-9
// 多段数据
Range: bytes=0-9, 30-39

接下来我们就分别来讨论着两种情况。

单段数据

HTTP/1.1 206 Partial Content
Content-Length: 10
Accept-Ranges: bytes
Content-Range: bytes 0-9/100

i am xxxxx

多段数据

HTTP/1.1 206 Partial Content
Content-Type: multipart/byteranges; boundary=00000010101
Content-Length: 189
Connection: keep-alive
Accept-Ranges: bytes


--00000010101
Content-Type: text/plain
Content-Range: bytes 0-9/96

i am xxxxx
--00000010101
Content-Type: text/plain
Content-Range: bytes 20-29/96

eex jspy e
--00000010101--

这个时候出现了一个非常关键的字段Content-Type: multipart/byteranges;boundary=00000010101,它代表了信息量是这样的:

  • 请求一定是多段数据请求
  • 响应体中的分隔符是 00000010101

因此,在响应体中各段数据之间会由这里指定的分隔符分开,而且在最后的分隔末尾添上- -表示结束。

猜你喜欢

转载自blog.csdn.net/HZ___ZH/article/details/113377741