## **斷點續傳**
什么是斷點續傳?
答:從那個點點斷開繼續傳。。。
額,看起來也沒毛病...
再問一遍,什么是斷點續傳?
答:
> 斷點續傳就是從文件上次中斷的地方開始重新下載或上傳,當下載或上傳文件的時候,如果沒有實現斷點續傳功能,那么每次出現異常或者用戶主動的暫停,都會去重頭下載,這樣很浪費時間。所以斷點續傳的功能就應運而生了。要實現斷點續傳的功能,需要客戶端記錄下當前的下載或上傳進度,并在需要續傳的時候通知服務端本次需要下載或上傳的內容片段。
>
說起到斷點續傳,他的關鍵點是什么?
* 如何告知服務器,從指定的位置下載
* 如何知道客戶端想要的指定位置是多少
其實,很簡單,并不需要我們去寫一些什么東西。HTTP協議本身就支持斷點續傳了。只要我們通過方式告訴服務器,從制定位置開始下載就可以了。
來來來,http協議搞起來~~~~~~
先來提問一下昂,說到http想到了什么???
(在線投骰子?還是誰能**~~自告奮勇~~**???)
```
/**
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
**/
```
## **Range & Content-Length & Content-Range & If-Range**
這些都是 HTTP 包中 Header 頭部的一些字段信息,其中 Range 和 If-Range 是請求頭中的字段,Content-Length 和 Content-Range 是響應頭中的字段。
### **Range**
當請求頭中出現 Range 字段時,表示告知服務端,客戶端下載該文件想要從指定的位置開始下載,至于 Range 字段屬性值的格式有以下幾種:
| 格式 | 含義 |
| --- | --- |
| Range:bytes=0-500 | 表示下載從0到500字節的文件,即頭500個字節 |
| Range:bytes=501-1000 | 表示下載從500到1000這部分的文件,單位字節 |
| Range:bytes=-500 | 表示下載最后的500個字節 |
| Range:bytes=500- | 表示下載從500開始到文件結束這部分的內容 |
當 app 想實現縮短大文件的下載耗時,可以開啟多個下載線程,每個線程只負責文件的一部分下載,當所有線程下載結束后,將每個線程下載的文件按順序拼接成一個完整的文件,這樣就可以達到縮短下載大文件的耗時目的了。
那么就可以使用`Range:bytes=501-1000`這種格式了,每個線程在各自的請求頭字段中,以這種格式加入相對應的信息即可達到目的了。
*****
如果 app 想實現斷點續傳,文件下載到一半被迫中斷,下次啟動還可以繼續接著上次進度下載時,那么此時可以使用 `Range:bytes=500-` 這種格式了,只要先獲取本地那份文件目前的大小,通過在請求頭中加入 Range 字段信息即可。
#### **Content-Length**
Content-Length 字段出現在響應頭中,用于告知客戶端此次下載的文件大小。
客戶端需要實現下載進度實時更新時,需要知道文件的總大小和目前下載的大小。
想要知道文件總大小? Content-Length中的字段就好
如果想要實現多線程同時分段下載大文件時,在下載前會發送一個不需要攜帶body信息請求,用于先獲取響應頭中的Content-Length字段
> * 如果這條鏈接是一次性將整個文件下載下來的,那么 Content-Length 就表示這個文件的總大小。
> * 如果這條鏈接指定了 Range,表明了只是下載文件的指定部分的內容,那么此時 Content-Length 表示的就只是這一部分的大小。
所以,如果客戶端實現了下載進度實時更新功能時,需要注意一下。因為如果文件是斷點續傳的,那么進度條的分母就不能用每次 HTTP 鏈接中的 Content-Length。要么下載前先發一條獲取用于文件總大小的請求,然后一直維護著這個數據,要么就使用 Content-Range 字段。
*****
#### **Content-Range**
Content-Range 字段也是出現在響應頭中,用于告知客戶端此鏈接下載的文件是哪個部分的,以及文件的總大小。
比如,當客戶端在請求頭中指定了`Range:bayes=501-1000`來下載一個總大小為 2000 字節文件的中間一部分內容時,此時,響應頭中的 Content-Range 字段信息如下:
`Content-Range:bytes 501-1000/2000`
斜杠前表示此鏈接下載的文件是哪一部分,斜杠后表示文件的總大小。
*****
#### **If-Range**
斷點續傳,說白點也就是分多次下載,既然不是一次性下載,那么就無法保證多次下載的間隔。
也就是說,有可能出現這種場景:
>這次由于某些原因只下載的一部分,而下次重啟繼續下載,但可能等到過了很多天后才重啟去繼續下載,如果在這期間,服務端的這份文件更新了怎么辦?
只要不是一次性下載的,那么就有可能會出現這種場景,顯然,這時候,就不希望斷點續傳了,而是要讓客戶端直接**重頭開始下載**,畢竟文件都已經發生更新了,不是同一份了,再繼續恢復下載也沒有什么意義。
**客戶端要如何知道服務端的文件是否發生變化,要重頭下載呢?**
這時就可以結合 If-Range 字段來實現了,這個也是在請求頭中的字段,跟 Range 字段一起使用,它的作用是給 Range 字段生效設置了一些條件,只有滿足這些條件,Range 才能生效。
也就是說,只有先滿足 If-Range,那么才能通過 Range 來實現斷點續傳。
那它的條件值可以設置為哪些呢?有兩種,Last-Modified 或者 ETag,這兩個也都是響應頭中的字段。**但不能將兩者同時使用。**
(if-Range)參考鏈接:[https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/If-Range](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/If-Range)
*****
#### **抓包:**
> 1\. https://www.taobao.com/sw.js
> 2. https://www.baidu.com/img/bd\_logo1.png?where=super
> 3.https://mat1.gtimg.com/pingjs/ext2020/qqindex2018/dist/img/qq\_logo\_2x.png
4.https://www.magicdatatech.com/filespath/files/20191104064048.png
鯊魚軟件的抓包示例:

首先先發起一個請求,設置了不攜帶 BODY 信息,這樣就可以在下載前先獲取到文件的總大小。

這是下載中斷后,重啟想要繼續下載時發起的請求信息,請求頭中指定了 `Range:bytes=12341380-` 表示本地已經下載了這么多,需要從這里開始繼續往下下載。
響應頭中返回了這部分的內容,并在 Content-Length 和 Content-Range 字段中給出了相關信息。
```
// 創建一個XMLHttpRequest對象
// IE6, IE5
// var xhr = new ActiveXObject("Microsoft.XMLHTTP");
// IE7+, Firefox, Chrome, Opera, Safari
var xhr = new XMLHttpRequest()
// 重置服務器端返回的類型
xhr.overrideMimeType('image/png')
// 初始化一個請求 參數3:表示該請求應該以`異步模式`執行。
xhr.open('GET', '[https://imgcps.jd.com/ling/7641991/5bmz5p2\_55S16KeG5beo5YiS566X/5L2g5YC85b6X5YWl5omL/p-5bd8253082acdd181d02f9d8/e129873b/590x470.jpg](https://imgcps.jd.com/ling/7641991/5bmz5p2_55S16KeG5beo5YiS566X/5L2g5YC85b6X5YWl5omL/p-5bd8253082acdd181d02f9d8/e129873b/590x470.jpg)', true)
// 超時時間,單位是毫秒
xhr.timeout = 2000;
// 設置 HTTP 請求頭的值。在`open()`之后、`send()`之前調用`setRequestHeader()`方法。
xhr.setRequestHeader('range', 'bytes=0-10')
// 發送請求。如果請求是異步的(默認),那么該方法將在請求發送后立即返回。
xhr.send()
// 只要 `readyState` 屬性發生變化,就會調用相應的處理函數。這個回調函數會被用戶線程所調用。當一個`XMLHttpRequest`請求被abort()方法取消時,其對應的 `readystatechange`事件不會被觸發。
xhr.onreadystatechange = function(){
if ( xhr.readyState == 4 && xhr.status == 206 ) {
console.info(xhr)
console.info( xhr.response);
} else {
console.info( xhr.statusText );
}
}
```
### **js 中的 Blob對象**
https://www.jianshu.com/p/b322c2d5d778