今天这篇文章和大家聊一聊如何做到只请求资源的一部分,这里需要用到几个http头——range、if-range、content-range、accept-range。
Range头信息介绍
Range主要用来设置获取数据的范围,格式如下:
Range: <unit>=<range-start>-<range-end>Range: <unit>=<range-start>-<range-end>, <range-start>-<range-end>, <range-start>-<range-end>
- <unit> 类型,一般来说是bytes;
- <range-start> 表示范围的起始值,一般是数字,如果不是数字就看服务端逻辑如何处理;
- <range-end> 表示范围的结束值。这个值是可选的,如果不存在,表示此范围一直延伸到文档结束,如果非数字,同上。
如: 获取 0-10字节的数据和15到结尾的数据
Range: bytes=0-10,15-
If-Range主要用来判断是否满足范围请求的条件,举个例子,假设昨天你用迅雷下载了一部电影但是没有下载完,今天你要接着下载,当再次下载时客户端就需要和服务器验证这部电影的资源内容有没有发生变化,If-Range在这里就是做验证使用的 。
Content-Range表示响应数据的内容范围,语法格式如下:
Content-Range: <unit> <range-start>-<range-end>/<size>Content-Range: <unit> <range-start>-<range-end>/*Content-Range: <unit> */<size>
- <unit> 类型,一般来说是bytes;
- <range-start> 区间的起始值;
- <range-end> 区间的结束值;
- <size> 整个文件的大小(如果大小未知则用 "*" 表示)
例如:
Content-Range: bytes10-15/22
Accept-Ranges用于服务器响应,告诉浏览器是否支持Range,
语法:
Accept-Ranges: bytesAccept-Ranges: none
- none不支持任何范围请求单位,由于其等同于没有返回此头部,因此很少使用。不过一些浏览器,比如IE9,会依据该头部去禁用或者移除下载管理器的暂停按钮;
- bytes 一般情况
代码实现
上面介绍了几个头信息的概念,下面我们用代码实现一下,大概流程如下:
我们还是以中间件的方式去实现。
如图2,我们通过range头获取请求的范围信息,如果类型合法,我们还需要处理范围数据,处理方式和处理url的query一样,在处理的过程中我们需要对不合法的范围进行纠正和过滤。如果类型不合法,我们就正常返回整个内容。
对于range范围内重叠和相邻的区域可以做一次合并,例如:
bytes=50-55,0-10,5-10,56-60,可以合并为[{start: 0, end: 10},{start: 50, end: 60}]。
如图3,如果range范围无效,我们返回状态码416,告诉客户端range是无效的,不满足要求。
如图4,如果range范围校验也没问题,我们还需要通过if-range提供的信息与etag或者Last-Modified做对比(对比二选一)。
- 如果if-range没有值,可以认为是无条件的,返回true;
- 如果和etag有一致的地方,证明资源未变,返回true,可以继续部分请求;
- 如果给的时间条件大于修改的时间,证明资源也未变,可以继续部分请求(大于还是小于,时间的意义可以自己定);
如果不满足if-range条件,继续走正常返回资源的逻辑,如果满足那就开始返回部分资源。
如图5,设置了状态为206,这是http标识部分内容返回的状态,另外还设置了accept-range和content-type。此处我们使用stream对内容进行分片,这里只返回了一段范围的内容。
对于多段请求,也是可以实现的,如下:
需要把content-type设置成multipart/byteranges; boundary=分隔符,这样的话就可以分片下载了。
总结
这篇文章主要介绍了range头相关的使用方法,内容还是蛮多的。本文的代码实现没那么全,主要讲了一下原理及流程,小伙伴们如果需要使用可以再打磨一下。