1. 程式人生 > 實用技巧 >Http系列:斷點續傳與多執行緒下載

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-:


返回的是89

Range條件請求

  • 如果客戶端已經得到了Range響應的一部分,並想在這部分響應未過期的情況下,獲取其他部分的響應
    常與If-Unmodified-Since或者If-Match頭部共同使用
  • If-Range = entity-tag / HTTP-date
    可以使用Etag或者Last-Modified

測試

下面用etag測試一下Range條件請求

首先獲取0-5位元組段

然後用-I來看看生成Hello 時伺服器生成Etag的值

接下來,用這個值放到If-Match中獲取6-10位元組段:World

如果Etag發生了變化,來看看結果會怎麼樣,將最後的0改為1


返回412 Precondition Failed

結論

通過條件請求可以判斷兩次下載之間,伺服器端資源有沒有發生變化。如果發生了變化,就可以通過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 = 1
    DIGIT // 完整資源的大小,如果未知則用*號替代
    bytr-range = first-byte-pos "-" last-byte-pos }

  • 例如:
    1、Content-Range: bytes 42-1233/1234
    2、Content-Range: bytes 42-1233/*

測試

用一個視訊播放的例子來看看206響應的樣子。

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