HTTP range 请求允许我们从服务器上只发送HTTP消息的一部分到客户端。这样的部分请求对于大型媒体、具有中断和恢复下载进度的下载文件请求很有帮助。

检查服务器是否支持 HTTP Range 请求

在进行HTTP range 请求之前,先检查服务器是否支持部分请求

如果请求一个资源时, HTTP响应中出现如下所示的 'Accept-Ranges', 且其值不是none, 那么服务器支持范围请求。

curl -I http://i.imgur.com/z4d4kWk.jpg
​
HTTP/1.1 200 OK
...
Accept-Ranges: bytes
Content-Length: 146515

在如上响应中,Accept-Ranges: bytes 代表可以使用字节作为单位来定义请求范围。这里的 Response Headers中的 Content-Length: 146515 则代表该资源的完整大小。

如果站点响应中未返回 Accept-Ranges 响应头,或者其值为none,那么这意味着server不支持HTTP range请求。

给服务器发HTTP Range请求

单范围请求

我们可以对一个资源发起单个范围请求:

curl http://i.imgur.com/z4d4kWk.jpg -i -H "Range: bytes=0-1023"

发出的请求如下:

GET /z4d4kWk.jpg HTTP/1.1 Host: i.imgur.com Range: bytes=0-1023

正常情况下 server 返回 206 部分内容响应:

HTTP/1.1 206 Partial Content Content-Range: bytes 0-1023/146515 Content-Length: 1024 ... (binary content)

这次并非检查server是否支持range请求,故Content-Length表示的是现在请求的范围大小,而Content-Range则表示的是这部分消息在完整资源中的位置。

多范围请求

curl http://www.example.com -i -H "Range: bytes=0-50, 100-150"

用逗号隔开多个范围,即可同时请求多部分资源。

响应如下:

HTTP/1.1 206 Partial Content Content-Type: multipart/byteranges; boundary=3d6b6a416f9b5 Content-Length: 282 --3d6b6a416f9b5 Content-Type: text/html Content-Range: bytes 0-50/1270

该响应有:

  • 206部分响应码:
  • Content-Type: multipart/byteranges;boundary=3d6b6a416f9b5——>表示遵循多部分 byterange

每个部分包含自己的Content-Type 和 Content-Range

条件范围请求

当继续请求更多资源时,你需要确保被存储的资源在上一帧收到后没有被改变。

If-Range HTTP请求创建了一个带条件的range HTTP请求,如果条件得到满足,range请求将会被发出,server 发回带有适当正文的206 partial content 应答,如果条件不满足则返回完整资源,并显示200 OK状态。这个头可以与Last-Modified 验证程序,或者与 ETag 一起使用。

If-Range: Wed, 21 Oct 2015 07:28:00 GMT

HTTP Range 请求响应

在处理HTTP Range 请求时,有三个相关的状态:

  • 206 Partial Content——> HTTP Range 请求成功
  • 416 Requested Range Not Satisfiable status.——> HTTP Range 请求超出界限
  • 200 OK——> 不支持范围请求

与分块相比

Transfer-Encoding 请求头允许分块编码,这在服务器给客户端发送大量的数据,且响应总大小直到请求结束才能确定时很有用,如果服务器直接发送数据给客户端而不缓存响应,或者确定具体响应大小的话,会产生延迟。HTTP Range 请求和分块是兼容的,一起用或者不一起用均可。

断点续传

所谓断点续传,也就是要从文件已经下载的地方开始继续下载。在以前版本的 HTTP 协议是不支持断点的,HTTP/1.1 开始就支持了。一般断点下载时才用到 Range 和 Content-Range 实体头。

Range

用于请求头中,指定第一个字节的位置和最后一个字节的位置,一般格式:

Range:(unit=first byte pos)-[last byte pos]

Content-Range

用于响应头,指定整个实体中的一部分的插入位置,他也指示了整个实体的长度。在服务器向客户返回一个部分响应,它必须描述响应覆盖的范围和整个实体长度。一般格式:

Content-Range: bytes (unit first byte pos) - [last byte pos]/[entity legth]

请求下载整个文件

  1. GET /test.rar HTTP/1.1
  2. Connection: close
  3. Host: 116.1.219.219
  4. Range: bytes=0-801 //一般请求下载整个文件是bytes=0- 或不用这个头

一般正常回应

  1. HTTP/1.1 200 OK
  2. Content-Length: 801
  3. Content-Type: application/octet-stream
  4. Content-Range: bytes 0-800/801 //801:文件总大小

假设你要开发一个多线程下载工具,你会自然的想到把文件分割成多个部分,比如4个部分,然后创建4个线程,每个线程负责下载一个部分,如果文件大小为403个byte,那么你的分割方式可以为:0-99 (前100个字节),100-199(第二个100字节),200-299(第三个100字节),300-402(最后103个字节)。

分割完成,每个线程都明白自己的任务,比如线程3的任务是负责下载200-299这部分文件,现在的问题是:线程3发送一个什么样的请求报文,才能够保证只请求文件的200-299字节,而不会干扰其他线程的任务。这时,我们可以使用HTTP1.1的Range头。Range头域可以请求实体的一个或者多个子范围,Range的值为0表示第一个字节,也就是Range计算字节数是从0开始的:
表示头500个字节:Range: bytes=0-499
表示第二个500字节:Range: bytes=500-999
表示最后500个字节:Range: bytes=-500
表示500字节以后的范围:Range: bytes=500-
第一个和最后一个字节:Range: bytes=0-0,-1
同时指定几个范围:Range: bytes=500-600,601-999

所以,线程3发送的请求报文必须有这一行:
Range: bytes=200-299

 服务器接收到线程3的请求报文,发现这是一个带有Range头的GET请求,如果一切正常,服务器的响应报文会有下面这行:

HTTP/1.1 206 OK
表示处理请求成功,响应报文还有这一行
Content-Range: bytes 200-299/403
斜杠后面的403表示文件的大小,通常Content-Range的用法为: . The first 500 bytes:
Content-Range: bytes 0-499/1234

 . The second 500 bytes: Content-Range: bytes 500-999/1234       . All except for the first 500 bytes: Content-Range: bytes 500-1233/1234       . The last 500 bytes: Content-Range: bytes 734-1233/1234
fca925969429c783bccc743c68367329-300x200.jpeg

A Student on the way to full stack of Web3.