前言
这几天在项目开发中,需要将项目放在Android手机中,并且实现播放本地视频。
但是,在开发过程中,我们发现播放中的本地视频,若再执行play()命令或者进行进度拖动,会重头进行播放。然而,我们将视频链接替换为云端地址,则能够正常操作。
为什么呢?
定位
经过排查,我们发现在前端操作逻辑一致,视频一致的情况下,仅app本地视频会出现这个问题。所以,我们近一步的比较了本地视频和云端视频的http协议,发现了不同。
本地视频的http协议:
云端视频的http协议:
经过对不同之处的比较分析,我们最后确认,视频无法拖动的问题和http协议中的Range请求有关。
HTTP Range规范
在HTTP/1.1协议中,通过Range请求,我们可以允许服务器只发送HTTP消息的一部分到客户端。这在传送大的媒体文件,或者与文件下载的断点续传功能搭配使用时非常有用。当然,也为我们视频播放器实时拖动提供了技术支持。
我们主要通过在Header里两个参数来实现,一个是在客户端发请求时设置Range,另一个是服务器端响应时对应Content-Range。
Accept-Ranges
在使用range请求前,我们需要先判断服务器是否支持Range请求。
假如在响应中存在 Accept-Ranges 首部(并且它的值不为 “none”),那么表示该服务器支持范围请求。
Range
range用于指示服务器应该返回文件的哪一或哪几部分。一般格式如下:
Range:(unit=first byte pos)-[last byte pos]
Range: 0-500 // 前500个字节
Range: -500 // 后500个字节
Range: 500- // 从501字节往后的所有字节
first byte pos表示需要内容的第一个字节的位置;last byte pos表示需要内容的最后一个字节的位置。
注:这里的字节位置从0开始
Content-Range
在发出Range请求后,服务器会在Content-Range返回中设置当前接受的范围和文件总大小。一般格式如下:
Content-Range: bytes (unit first byte pos) - [last byte pos]/[entity legth]
entity legth表示完整资源的大小,如果未知则用*号替代;其他内容同Range请求格式。
Content-Type
Content-Type用于指示资源的MIME类型。在多重范围请求中,取值固定为multipart/byteranges;boundary=xxx(多重范围见下文)。
Content-Length
Content-Length一般是用于表示请求资源的大小,但若我们使用Range请求,Content-Length用于表示请求范围内资源的大小。
多重范围
Range也支持一次请求文档的多个部分,即多重范围。这里我们需要用逗号将请求范围分隔开。
Range: bytes=0-500, 1000-1500
当多重范围返回成功后,服务器会将Content-Type返回设置为multipart/byteranges; boundary=xxx,表示这个响应有多个byterange。并且,每一部分byterange都有他自己的Centen-type头部和Content-Range,并且使用boundary参数进行划分。
// request
Range: bytes=0-50, 100-150
// response
Content-Type: multipart/byteranges; boundary=3d6b6a416f9b5
--3d6b6a416f9b5
Content-Type: text/html
Content-Range: bytes 0-50/1270
--3d6b6a416f9b5
Content-Type: text/html
Content-Range: bytes 100-150/1270
--3d6b6a416f9b5--
条件式范围请求
我们在下载/上传文件的过程中,使用断点续传,将文件分为几个部分。那么,在发生网络故障的时候,可以从之前下载/上传的地方开放,而不需要重新下载/上传,从而节约了时间。
当我们重新开放下载文件时,必须要保证服务端的文件没有被修改过。这里我们使用If-Range请求来进行条件式范围请求,若条件满足则返回相应的相应的消息主体,进行断点续传;若不满足,则重新下载新的文件。
-
If-Range的值既可以用Last-Modified时间值用作验证,也可以用ETag标记作为验证,但不能将两者同时使用。 -
If-Range必须与Range配套使用。如果请求报文中没有Range,那么If-Range就会被忽略。如果服务器不支持If-Range,那么Range也会被忽略。
状态码
- 请求成功,返回
206 Partial Content。 - 不支持范围请求,返回
200 OK。 - 请求的范围越界,即范围值超过了资源的大小,返回
416 Requested Range Not Satisfiable。
总结
- 我们使用
Range请求和Content-Range返回来实现范围请求和多范围请求 - 通过
If-Range请求来实现条件范围请求 - 范围请求成功返回状态码206,不支持范围请求返回状态码200,请求范围超出返回状态码416