Http系列:斷點續傳與多執行緒下載
前言
當下載電影時,我常常會想中斷下載後,為什麼點選開始時會在中斷的地方繼續下載呢?
又或者在看線上電影時,為什麼可以按著播放條拖動就能看到想看的片段呢?
http的range請求將解決以上困惑。
多執行緒、斷點續傳、隨機點播等的場景的步驟
1、客戶端明確任務:從哪開始下載
- 本地是否已有部分檔案:檔案已下載部分在伺服器端發生改變?
- 使用幾個執行緒併發下載
2、下載檔案的指定部分內容
3、下載完畢後拼裝成統一的檔案
HTTP Range規範
在RFC7233中有詳細介紹
1、允許伺服器基於客戶端的請求只發送響應包體的一部分給到客戶端,而客戶端自動將多個片段的包體組合成完整的體積更大的包體。
- 支援斷點續傳
- 支援多執行緒下載
- 支援視訊播放器實時拖動
2、伺服器通過Accept-Range頭部表示是否支援Range請求
- Accept-Ranges = acceptable-ranges
- 例如:
Accept-Ranges: bytes: 支援;
Accept-Ranges: none: 不支援
Range請求範圍的單位
基於位元組為單位的時候,舉例:設定響應體長度為10000
- 第1個500位元組:
bytes=0-499 // 從0開始 - 第2個500位元組:
bytes=500-999
bytes=500-600, 601-999
bytes=500-700, 601-999 - 最後1個500位元組
bytes=-500
bytes=9500- - 僅要第一個和最後一個位元組:bytes=0-0, -1
通過Range頭部傳遞請求範圍,如:Range: bytes=0-499
測試
下面用一些小例子有測試一下。
用node搭了一個簡單的伺服器,返回的數字是22個位元組的響應體
'Hello World 0123456789';
現在用curl命令獲取全部的響應體,然後訪問0-5的位元組段:
-H引數新增 HTTP 請求的標頭。
上面的命令就是新增HTTP頭Range: bytes=0-5。
返回的是Hello (加上空格)一共六個位元組。
現在獲取第21個位元組及以後的位元組段,就可以用20-:
Range條件請求
- 如果客戶端已經得到了Range響應的一部分,並想在這部分響應未過期的情況下,獲取其他部分的響應
常與If-Unmodified-Since或者If-Match頭部共同使用 - If-Range = entity-tag / HTTP-date
可以使用Etag或者Last-Modified
測試
下面用etag測試一下Range條件請求
然後用-I來看看生成Hello 時伺服器生成Etag的值
接下來,用這個值放到If-Match中獲取6-10位元組段:World
如果Etag發生了變化,來看看結果會怎麼樣,將最後的0改為1
結論
通過條件請求可以判斷兩次下載之間,伺服器端資源有沒有發生變化。如果發生了變化,就可以通過412這個響應知道,資源已經發生了變化。
伺服器響應
如果只獲取部分的body,那麼伺服器端返回的響應碼不是200,而是206。
206 Partial Content
-
Content-Range頭部:顯示當前片段響應體在完整包體中的位置
-
Content-Range = byte-content-range / other-content-range
{ btye-content-range = bytes-unit SP (byte-range-resp / unsatisfied-range)
byte-range-resp = byte-range '/' (complete-length / '')
complete-length = 1DIGIT // 完整資源的大小,如果未知則用*號替代
bytr-range = first-byte-pos "-" last-byte-pos } -
例如:
1、Content-Range: bytes 42-1233/1234
2、Content-Range: bytes 42-1233/*
測試
416 Range Not Statisfiable
- 請求範圍不滿足實際資源的大小,其中Content-Range中的complete-length顯示完整響應的長度,例如
Content-Range: bytes */1234
測試
如果獲取範圍超出實際資源的大小,比如獲取30-40。返回416
200 OK
- 伺服器不支援Range請求時,則以200返回完整的響應包體
多重範圍與multipart
- 請求:
Range: bytes=0-50, 100-150 - 響應:
Content-Type: multipart/byteranges; boundary=...
測試
獲取5-10, 10-15片段。
總結
1、客戶端通過Range頭部傳遞請求範圍
2、服務端返回Accept-Range頭部表示是否支援Range請求。
3、客戶端如果在得到Range響應的一部分,並想在這部分響應未過期的情況下,獲取其他部分的響應,可以用If-Range頭部使用Etag或者Last-Modified為值。
4、只獲取部分的body,伺服器返回206響應碼,其中Content-Range頭部顯示當前片段響應體在完整包體中的位置
5、客戶端想多重範圍下載資源,在Range頭部的格式為Range: bytes=0-50, 100-150...(用逗號分隔)
響應頭部Content-Type: multipart/byteranges; boundary=...
作者: zhangwinwin
連結:Http系列:斷點續傳與多執行緒下載
來源:github