IOS/Mac/Safari无法播放MP4文件流及Http1.1 Range分段请求相关
故事背景
公司项目需要将多媒体文件迁移至阿里OSS,原本直接从后端静态文件的<img>
<video>
等标签都要改成从OSS获取展示。
于是乎拦截了所有静态链接,调用了OSS接口获取了文件流返回到前台进行展示,简单粗暴,但是也遇到了问题 —— ios的 <video>
标签全军覆没。
由于项目里视频比较少,这问题还是在上线前三天发现的,那个急的,一开始还找不到是什么问题,找到老版本的代码后发现前端代码毫无区别(亏得还在前端调试了半天),最后发现使用静态文件就没什么问题。
确定下来大致是文件流的缘故没跑了,百度+谷歌发现safari不支持整个文件流,服务器必须支持分段请求,也就是下面要说的Range分段请求了。
请求头 Range
和 响应头 Content-Range
Http协议从1.1开始支持获取文件的部分内容,这为并行下载以及断点续传提供了技术支持。该标准分为两个Header,分别为一个请求头Range
, 和一个响应头Content-Range
。
请求头 Range
先来看看请求头,safari对于文件流的请求会带上这个头,服务器需要正确的对其作出响应,浏览器才能正确的展示文件。之前没法播放文件流正是因为没有正确响应这个请求头。
格式
该请求头的格式为Range: bytes=开始字节-结束字节
示例:Range:bytes=0-499
表示请求开始的500个字节除了基本格式以外,
Range
头还有可能是以下格式:
表示第二个500字节:Range:bytes=500-999
表示最后500个字节:Range:bytes=-500
表示500字节以后的范围:Range:bytes=500-
第一个和最后一个字节:Range:bytes=0-0,-1
同时指定几个范围:Range:bytes=500-600,601-999
如何回复
对于Range
头,服务器需要作出正确的响应,返回对应的状态码来告知客户端,服务器是否支持分段请求:200
:不支持分段请求,但是能正常响应。206
:支持分段请求 ,并返回分段结果,此时还需要在响应头中添加Content-Range
头,并且返回对应的字节片段。
即:如果我们需要正确的响应分段请求,需要做以下几步
- 解析
Range
请求头,获取客户端想要的文件片段范围 - 返回
206
状态码 - 响应头中
Content-Type
头需要填写为文件mime类型,如video/mp4
- 响应头中添加
Content-Range
头,告诉客户端字节片段的信息(包含字节起止位置及文件流总大小) - 响应体中添加客户端所请求的字节片段
响应头 Content-Range
上一步讲到我们除了需要返回206
状态码及返回字节片段以外,还需要返回一个Content-Range
头,来告诉客户端我们返回了哪一部分的字节片段,来看一下这个响应头相关的一些信息。
格式
还是一样先来看格式,Content-Range: bytes 起始字节-末尾字节/总字节数
示例:Content-Range:bytes 0-499/13521
表示文件总共有13521字节, 本次返回了0-499字节针对不同格式的
Range
头,我们的响应内容也会有相应的变化(*注意,这里一定不是照搬Range
头的内容)
对于Range:bytes=500-999
,应该返回Content-Range:bytes 500-999/13521
对于Range:bytes=-500
,应该返回Content-Range:bytes 13021-13520/13521
对于Range:bytes=500-
,应该返回Content-Range:bytes 500-13520/13521
对于
Range:bytes=0-0,-1
和Range:bytes=500-600,601-999
这样的多个范围,Content-Type
头需要修改为multipart/byteranges
,并且在响应体内返回每个范围对应的多个Content-Range
和Content-Type
,这里具体是怎样实现,由于上线比较着急,用到多个范围的场景也非常少,所以没有时间验证及研究,后面再来做补充,如果有大牛能告知这里应该怎么做的,希望能在评论区告知一下,提前谢谢啦!
代码我就不上了,每个语言每个框架都不一样,大家理解了之后按照以下步骤来书写代码,应该不会有什么大问题的。
- 判断请求中是否有
Range
头,如果没有,就按普通请求处理,返回整个流 - 如果有
Range
头,则解析Range
的内容,获得所需片段的起止位置 - 设置响应状态码为
206
- 设置响应头
Content-Type
为文件mime类型 - 设置响应头
Content-Range
为正确格式的分段起止位置与文件流总字节数 - 根据分段起止位置截取文件流,写入响应体
- 结束该次请求
有什么问题欢迎在下面评论,我会及时回复www