HTTP 传输大文件的方法

传输大文件的方法

数据压缩

在上面我们已经讲过 HTTP 传输数据时,有一个字段是 Accept-Encoding,它代表客户端支持的数据压缩的格式,这样服务器就可以选择其中一种,放到 Content-Encoding 中,再把原来的数据压缩后发送给浏览器。

如果压缩率有50%,那么原来100k 的数据就可以压缩成50k 大小,极大地提升传输效率。

这种压缩方式非常适合文本如(text/html)的压缩,不过并不适合传输图片、音频视频等数据,因为它们本身已经高度压缩了。

分块运输

除了数据压缩之外,还可以把大文件整体变小,分解成很多个小块,这样就可以把小块分发给浏览器,浏览器收到后复原。

这种方式有个好处,每次只收发一小部分,网络不会被大文件长时间占用,可以节省带宽资源。

这种方法在 HTTP 中叫 chunked 分块传输,在响应报文里用头字段 Transfer-Encoding:chunked 来表示。意思是报文的 body 可以分成多次发送。

分块传输可以用于流式数据,例如数据库动态生成的表单页面,这种情况下 body 数据的长度是未知的,无法在头字段 Content-Length 里给出确切的长度,所以也只能用 chunked 方式分块发送。

“Transfer-Encoding: chunked”和“Content Length”这两个字段是互斥的,也就是说响应报文里这两个字段不能同时出现,一个响应报文的传输要么是长度已知,要么是长度未知(chunked)。

范围请求

分块传输就是把大文件分成很多个小块,那么假设我希望获取大文件中特定的片段数据,显然没办法用分块传输做到。

HTTP 协议中,还有一种范围请求可(range requests)的概念,允许客户端在请求头里面使用专用字段来表示只获取文件的一部分。

在做范围请求前,需要 Web 服务器在响应头上使用字段 Accept-Ranges:bytes,明确告知客户端支持范围请求。不支持则可以不传这个字段或者设置为 none

请求头 Range 是 http 范围请求的专用字段,格式是 bytes=x-y,其中 x和 y 是以字节为单位的数据范围。

需要注意 x、y 表示的是偏移量,范围从0计算,比如前10个字节表示为0-9.

其中 x 和 y 是可以省略的,0-表示文档起点到终点,-1表示文档最后一个字节,-10表示从文档末尾倒数10个字节。

服务器收到 Range 字段后,需要做四件事

  • 检查范围是否合法。不合法可以返回416编码。

  • 范围正确,服务器需要根据 Range 头计算偏移量,读取文件的片段,返回206 Partial Content,表示 body 只是数据的一部分

  • 服务器需要加上响应头 Content-Range,告诉片段的实际偏移量和资源的总大小,格式则是bytes x-y/length,和请求头的 Range 字段区别是没有=号且范围后多了总长度。

  • 最后发送数据了

范围请求的常见应用是视频的拖拽进度和多段下载、断点续传等。

多段数据

范围请求还支持一次获取多个片段数据,可以在 Range 中使用多个x-y

这种情况需要一种特殊的 MIME 类型:multipart/byteranges,表示报文的 body 是由多段字节序列组成的,并且还要给一个参数 boundary=xxx给出段之间的分割标记。

image.png

小结

压缩 HTML 等文件是传输大文件的基本方法

分块传输可以流式收发数据,节省内存和带宽。使用响应头 Transfer-Encoding:chunked

范围请求可以只获取部分数据。主要应用于断点续传和视频拖拽等。使用请求头字段 Range 和响应头字段 Content-Range。相关响应状态码416和206

也可以一次性请求多个范围,此时响应报文的Content-type 是multipart/byteranges,body 部分多个部分会用 boundary 字符串分隔。