新聞中心
注意,本文所說(shuō)的斷點(diǎn)續(xù)傳特指 HTTP 協(xié)議中的斷點(diǎn)續(xù)傳。本文主要聊聊思路和關(guān)鍵代碼,更多細(xì)節(jié)請(qǐng)參考本文附帶的 demo。
工作原理
HTTP 協(xié)議中定義了一些請(qǐng)求/響應(yīng)頭,通過(guò)組合使用這些頭信息。我們可以在一次 HTTP 請(qǐng)求中只請(qǐng)求一個(gè)文件中的一部分?jǐn)?shù)據(jù)。這樣我們就可以把已經(jīng)下載的數(shù)據(jù)存起來(lái),下次只用請(qǐng)求剩余的數(shù)據(jù)即可,當(dāng)全部數(shù)據(jù)都下載到本地后再完成合并工作。
HTTP 協(xié)議指出,可以通過(guò) HTTP 請(qǐng)求中的 Range 頭指定請(qǐng)求數(shù)據(jù)的范圍,Range 頭的使用也很簡(jiǎn)單,只要指定下面的格式就可以了:
Range: bytes=500-999
它的意思是,只請(qǐng)求目標(biāo)文件的第 500 到第 999 這 500 個(gè)字節(jié)。
比如我有一個(gè)1000 bytes 大小的文件需要下載,第一次請(qǐng)求時(shí)不用指定 Range 頭,表示下載整個(gè)文件。但在下載完第 499 個(gè)字節(jié)后,下載被取消了。那么在下一次請(qǐng)求下載同一個(gè)文件時(shí),只需要下載第 500 個(gè)字節(jié)至第 999 個(gè)字節(jié)的數(shù)據(jù)就可以了。原理看上去很簡(jiǎn)單,但我們需要考慮下面幾個(gè)問(wèn)題:
1. 是不是所有的 web 服務(wù)器都支持 Range 頭?
2. 多次請(qǐng)求之間可能會(huì)間隔很長(zhǎng)的時(shí)間,服務(wù)器上的文件發(fā)生了變化怎么辦?
3. 如何保存下載的部分?jǐn)?shù)據(jù)和相關(guān)信息?
4. 當(dāng)我們通過(guò)字節(jié)操作把一個(gè)文件拼成原始大小后,如何驗(yàn)證它和源文件一模一樣?
下面我們就帶著這些問(wèn)題去探究斷點(diǎn)續(xù)傳的一些細(xì)節(jié)。
檢查服務(wù)器端對(duì)斷點(diǎn)續(xù)傳的支持
在服務(wù)器響應(yīng)我們的請(qǐng)求時(shí),會(huì)在響應(yīng)頭中通過(guò) Accept-Ranges 指明是否接受請(qǐng)求一個(gè)資源的一部分?jǐn)?shù)據(jù)。但這里似乎有個(gè)小小的陷阱,就是不同的服務(wù)器可能返回不同的值來(lái)指明自己能夠接受部分資源的請(qǐng)求。貌似比較統(tǒng)一的方法是,當(dāng)服務(wù)器不支持請(qǐng)求部分?jǐn)?shù)據(jù)時(shí),都會(huì)返回 Accept-Ranges: none,我們只要判斷這個(gè)返回值是不是等于 none 就行了。代碼如下:
private static bool IsAcceptRanges(WebResponse res) { if (res.Headers["Accept-Ranges"] != null) { string s = res.Headers["Accept-Ranges"]; if (s == "none") { return false; } } return true; }
檢查服務(wù)器端文件是否變化
當(dāng)我們下載了一個(gè)文件的一部分之后,可能馬上就會(huì)接著下載,也可能會(huì)過(guò)一段時(shí)間再下載,也可能永遠(yuǎn)不會(huì)再接著下載了…
這里的問(wèn)題是,當(dāng)下次要接著下載時(shí),如何確定服務(wù)器上的文件還是當(dāng)初下載了一半的那個(gè)文件。如果服務(wù)器上的文件已經(jīng)更新了,那無(wú)論如何都需要重新從頭開(kāi)始下載。只有在服務(wù)器上的文件沒(méi)有發(fā)生變化的情況下,斷點(diǎn)續(xù)傳才有意義。
對(duì)于這個(gè)問(wèn)題,HTTP 響應(yīng)頭為我們提供了不同的選擇。ETag 和 Last-Modified 都能完成任務(wù)。
先看 ETag:
The ETag response-header field provides the current value of the entity tag for the requested variant. (引自RFC2616 14.19 ETag)
簡(jiǎn)單點(diǎn)說(shuō) ETag 就是一個(gè)標(biāo)識(shí)當(dāng)前請(qǐng)求內(nèi)容的字符串,當(dāng)請(qǐng)求的資源發(fā)生變化后,對(duì)應(yīng)的 ETag 也會(huì)變化。好了,最簡(jiǎn)單的辦法是第一次請(qǐng)求時(shí),把響應(yīng)頭中的 ETag 存下來(lái),下次請(qǐng)求時(shí)做比較。代碼如下:
string newEtag = GetEtag(response);// tempFileName指已經(jīng)下載到本地的部分文件內(nèi)容// tempFileInfoName指保存了Etag內(nèi)容的臨時(shí)文件if (File.Exists(tempFileName) && File.Exists(tempFileInfoName)) { string oldEtag = File.ReadAllText(tempFileInfoName); if (!string.IsNullOrEmpty(oldEtag) && !string.IsNullOrEmpty(newEtag) && newEtag == oldEtag) { // Etag沒(méi)有變化,可以斷點(diǎn)續(xù)傳 resumeDowload = true; } }else{ if (!string.IsNullOrEmpty(newEtag)) { File.WriteAllText(tempFileInfoName, newEtag); } }private static string GetEtag(WebResponse res) { if (res.Headers["ETag"] != null) { return res.Headers["ETag"]; } return null; }
再來(lái)看看 Last-Modified:
The Last-Modified entity-header field indicates the date and time at which the origin server believes the variant was last modified. (引自RFC2616 14.29 Last-Modified)
Last-Modified 就是所請(qǐng)求的資源在服務(wù)器上的最后一次修改時(shí)間。使用方法和 ETag 大體相同。
個(gè)人感覺(jué)使用 ETag 和 Last-Modified 中的任何一個(gè)都能達(dá)到我們的目的。但是你也可以兩個(gè)都用,做 double check,誰(shuí)知道web服務(wù)器的實(shí)現(xiàn)是不是嚴(yán)格遵循了 HTTP 協(xié)議!
保存中間結(jié)果
這里主要就是用 C# 進(jìn)行文件操作。大體思路是如果有未下載完的文件,就把新下載的字節(jié)添加到文件的末尾,不再啰嗦,有興趣的同學(xué)請(qǐng)直接看 demo 代碼。
驗(yàn)證文件
在斷點(diǎn)續(xù)傳的過(guò)程中,我們以 byte 為單位下載、合并文件,如果整個(gè)過(guò)程中稍有沒(méi)有處理好的異常,可能最后得到的文件就和源文件不太一樣。因此最好是能夠?qū)ο螺d好的文件進(jìn)行一次校驗(yàn)??蛇@也是最難、最不容易實(shí)現(xiàn)的。因?yàn)樗枰?wù)器端的支持,比如服務(wù)器端在提供一個(gè)可下載文件的同時(shí)提供該文件的 MD5 hash。當(dāng)然,如果服務(wù)器端也是我們自己創(chuàng)建的,我們就可以去實(shí)現(xiàn)它。但我們又怎么能夠要求現(xiàn)存的 web 服務(wù)器都提供這樣的功能呢!
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)cdcxhl.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。
網(wǎng)站題目:C#文件下載之?dāng)帱c(diǎn)續(xù)傳-創(chuàng)新互聯(lián)
文章URL:http://www.dlmjj.cn/article/dohjdh.html