把大象装进冰箱

142 阅读4分钟

在HTTP诞生之初,传输的都是文本,最多也就几K,经过互联网不断的发展,传输的格式也逐渐丰富:从图片到音频,再到视频,格式多种多样,传输文件的大小也不断在增加,现在一部高清电影或电视剧都是按G来的。对我们100M带宽或4G网络来说,传输这么大的文件就有点捉襟见肘了,链路会被挤的很满,传输时间也很慢。在这种背景下,就需要我们用一些辅助方法来帮助文件传输,整头大象是不能直接装进冰箱的。

HTTP大文件的传输

数据压缩

首先我们想到的应该是把数据压缩,就像我们平时传给同事文件,如果是大件,我们肯定也会先压缩再传输,减少传输时间。

在前面我们提到过一个报文字段Accept-Encoding,里面放入客户端期望的压缩方式,服务器就会尽量满足客户端的要求,在Content-Encoding字段中返回是如何压缩的。

但需要注意的是,压缩对文本来说收益是非常大的,但对音频视频,压缩后可能不会小太多,因为它们本身就是被高度压缩过的。

分块传输

数据压缩对文本虽然很友好,但对视频等大文件却爱莫能助,那还有什么方式可以大变小呢,不能压缩,那就只能分块了,化整为零,也是间接减小文件。

分块传输有什么好处呢?它是把大文件分割成一个个的小文件。但文件的总体大小没有变化,这样在传输过程中不会被大文件一直占用,内存中也不需要保存这么大的资源,就相当于一方化整为零的输入,另一方化零为整的拼接还原。

在响应报文中有个Transfer-Encoding:chunked,表示的意思就是文件不是一次性传输的,而是分块传输。这个字段和Content-Length两者互斥,不会同时出现。这是为什么呢?

Content-Length表示的是响应内容的长度,而Transfer-Encoding:chunked表示的含义是分块,前者代表的是整个响应,而后者是分块,自然不知道全部响应内容,所以两者不会同时出现在响应头字段中。

范围请求

在看视频的时候,如果某段很无聊,想跳过,这时候我们就会往后拉进度条。而数据压缩分块传输技术就爱莫能助了。拉进度条相当于要请求整段视频的某个片段,我们是不是要在请求报文中告诉服务器请求的是哪个片段,然后服务器做出响应。这就是范围请求

范围请求不是每个服务器必备的功能,可以支持也可以不支持,在响应头中返回Accept-Ranges: bytes,表示服务器支持范围请求;如果返回的是Accept-Ranges: none或索性没返回,则表示服务器不支持范围请求。

发送范围请求需要请求头中添加Range字段,值的格式是bytes=x-yxy是以字节为单位的范围数据,如:

Range: bytes=0-9 // 取前10个字节数据

并且x,y可以省略其一,如 0-就是表示从0开始到最后一个字节,其实就是整个文件了。

多段数据

多段数据就是可以同时响应多个请求,返回多个片段。格式是这样的:

Range:tytes=100-200,1000-2000  // 请求头

在请求头中Range字段如果是这样的值,则会请求多段数据。

如果服务器正常响应了多段数据,那在响应头中的Content-Type字段值是Multipart/byteranges,并且还会新增个参数boundary,如下:

ontent-Type: multipart/byteranges; boundary=00000000001

在每段中都会有boundary作为分隔符来隔离开来:

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

data1
--00000000001
Content-Type: text/plain
Content-Range: bytes 20-29/96

data2
--00000000001--

并且这种正常的范围请求服务器的状态码是206 Partial Content,但如果服务器无法响应范围请求的话,则返回的状态码是200.