HTTP实现大文件传输原理
业务背景
在编写视频播放功能时,没想明白如何实现大体积视频的一边加载一边播放,这里实际上是利用了HTTP所实现的分块传输,也可以称之为断点续传,下面介绍几个涉及到的关键字段。
Transfer-Encoding: chunked
对于响应报文,存在两种情况:报文长度已知、报文长度未知;这分别对应这响应头里Content-Length、Transfer-Encoding: chunked这两个互斥的字段。
Content-Length给出响应报文确切的长度,而Transfer-Encoding: chunked表示报文的body体不是一次性发过来的,而是被分割成多个模块逐个发送。
既然是互斥的,也就是说chunked编码是长度未知的,chunked编码在“流式”收发数据时,通常数据是即时生成的(即动态数据),例如压缩下载,是压缩一部分,传输一部分,报文总长度只有全部压缩完才知道。
分块传输的编码规则有以下四步:
1.每个chunk包含长度头和数据块两部分;
2.长度头是以 CRLF(回车换行,即\r\n)结尾的一行明文,用 16 进制数字表示长度;
3.数据块紧跟在长度头后,最后也用 CRLF 结尾,但数据不包含 CRLF;
4.最后用一个长度为 0 的块表示结束,即“0\r\n\r\n”。
过程如下图:
Accept-Ranges: bytes
作用就是告诉浏览器,web服务器是支持范围请求的,比如说看视频时跳过片头,或者直接点击到中间部分开始播放,实际上点击之前的chunk就不会被加载了
Range: bytes=x-y
Range是范围请求的专用字段,x和y是以字节为单位的数据范围,e.g
“0-” 表示从文档起点到终点,即整个文件;
“10-” 表示第10个字节到文档终点;
“-1” 表示文档最后一个字节;
“-10” 表示文档倒数10个字节
以下图为例:
Status Code: 206 Partial Content
状态码206与200类似,都表示请求成功,但是body体只是原数据的一部分
Content-Range: bytes 107413504-330051762/330051763
这里描述了该文件片段的起始字节和结束字节以及资源的总大小
条件请求
常用的条件请求有If-Modified-Since和If-None-Match,他们要求服务器在第一次请求的响应报文中提供Last-Modified和ETag;这样第二次请求就可以带上缓存中的值,用来比较资源是否为最新的。如果资源没有变化,则返回304 Not Modified;表示缓存依然有效,浏览器可以更新有效期继续使用。
Last-Modified: Tue, 26 Mar 2019 07:20:03 GMT
标识文件最后修改时间,这样可以用来判断续传文件是否发生过改动
ETag: “1853a25a02d9ba23f96408d66891ed51”
Etag(Entity Tags)主要为了解决 Last-Modified 无法解决的一些问题。
1.一些文件可能会周期性的更改,但是内容并不改变(仅改变修改时间),这时候我们并不希望客户端认为这个文件被修改了,而重新 GET;
2.某些文件修改非常频繁,例如:在秒以下的时间内进行修改(1s 内修改了 N 次),If-Modified-Since 能检查到的粒度是 s 级的,这种修改客户端无法判断(或者说 UNIX 记录 MTIME 只能精确到秒)。
3.某些服务器不能精确的得到文件的最后修改时间。为此,HTTP/1.1 引入了 Etag。Etag 仅仅是一个和文件相关的标记位,需要放在 “” 内,可以是一个版本标记,例如:v1.0.0;也可以是一串编码 “627-4d648041f6b80”;类似于git里打的tag标签。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!