该功能在 HTTP Range规范(RFC7233)中声明

设计三个http状态码:

  1. 206 Partial Content:部分返回
  2. 416 Range Not Satisfied:范围不满足
  3. 412 Precondition failed:之前的数据已过期

思考实现以下功能:

  1. 客户端如何下载数据的片段?(断点续传,多线程下载,视频播放器实时拖动)
  2. 如何检测客户端已下载的部分本地文件是否在服务端发生了改变?
  3. 客户端一次请求如何同时下载多个片段的数据?
  4. 服务端如何告知客户端它支持Range请求

客户端如何下载数据的片段?

通过Range请求头,告知服务端此次请求的字节范围,使用方法如下:

  1. 请求单片段字节:Range: bytes=0-499
  2. 请求多片段字节:Range: 500-600,601-999,多重范围响应头中Content-Type: multiple/byteranges; boundary=...,响应体中使用boundary分隔
  3. 仅要第1个和最后1个字节:Range: 0-0, -1

下载多个片段时的返回数据

发起Range: bytes=0-5,10-15的请求时:
下载多个片段数据时,响应头中会返回Content-Type: multiple/byteranges; boundary=00000000000000006097;
然后响应体如下:

  1. --00000000000000006097
  2. Content-Type: text/plain; charset=utf-8
  3. Content-Range: bytes 0-5/27
  4. abcdef
  5. --00000000000000006097
  6. Content-Type: text/plain; charset=utf-8
  7. Content-Range: bytes 10-15/27
  8. klmnop
  9. --00000000000000006097--

服务端如何告知客户端它支持Range请求?

通过Accept-Ranges: bytes | none响应头,bytes表示支持,none表示不支持

客户端如何判断已缓存的部分是否过期?

把已响应的响应头中Etag作为下次请求的Is-Match请求头传过去,如果服务端判断数据已过期,则返回

  1. 发起 0-5 字节的请求:
  2. curl protocol.taohui.tech/app/letter.txt -H 'Range: bytes=0-5' - I
  3. 拿到响应头中的 Etag
  4. 再次发起 6-10 字节的请求:
  5. curl protocol.taohui.tech/app/letter/txt -H 'Range: bytes=6-10' -H 'If-Match:5cc3f0b5-1b'

验证本地数据未过期,则第二次请求正常返回数据,如本地数据已过期,则返回412 Precondition Failed

Is-Unmodified-SinceIs-Match
If-Range Etag或者Last-Modified

请求数据不满足

比如数据总共26字节,发起请求Range: bytes=30-50,此时数据不满足,返回状态码416 Range Not Satisfiable
如果服务器不支持Range请求时,则以200返回完整的响应包体

视频分片播放实战

  1. 请求接口:
  2. https://f.video.weibocdn.com/u0/GjoQbWbAgx07WRNUrpa0010412008N5g0E010.mp4?label=mp4_720p&template=576x1280.24.0&media_id=4780667238809610&tp=8x8A3El:YTkl0eM8&us=0&ori=1&bf=5&ot=v&lp=00001qykEd&ps=mZ6WB&uid=ziYazFH&ab=7397-g1,8012-g2,6377-g0,8013-g0,3601-g28,1258-g0&Expires=1655890856&ssig=Sb%2F%2Bdv8p%2Bq&KID=unistore,video
  3. 设置请求头:
  4. Range: bytes=0-100000