欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C# 文件下載之?dāng)帱c(diǎn)續(xù)傳實(shí)現(xiàn)代碼

 更新時(shí)間:2017年01月12日 15:50:44   作者:sparkdev  
本篇文章主要介紹了C# 文件下載之?dāng)帱c(diǎn)續(xù)傳實(shí)現(xiàn)代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

注意,本文所說的斷點(diǎn)續(xù)傳特指 HTTP 協(xié)議中的斷點(diǎn)續(xù)傳。本文主要聊聊思路和關(guān)鍵代碼,更多細(xì)節(jié)請參考本文附帶的 demo。

工作原理

HTTP 協(xié)議中定義了一些請求/響應(yīng)頭,通過組合使用這些頭信息。我們可以在一次 HTTP 請求中只請求一個(gè)文件中的一部分?jǐn)?shù)據(jù)。這樣我們就可以把已經(jīng)下載的數(shù)據(jù)存起來,下次只用請求剩余的數(shù)據(jù)即可,當(dāng)全部數(shù)據(jù)都下載到本地后再完成合并工作。

HTTP 協(xié)議指出,可以通過 HTTP 請求中的 Range 頭指定請求數(shù)據(jù)的范圍,Range 頭的使用也很簡單,只要指定下面的格式就可以了:

Range: bytes=500-999

它的意思是,只請求目標(biāo)文件的第 500 到第 999 這 500 個(gè)字節(jié)。

比如我有一個(gè)1000 bytes 大小的文件需要下載,第一次請求時(shí)不用指定 Range 頭,表示下載整個(gè)文件。但在下載完第 499 個(gè)字節(jié)后,下載被取消了。那么在下一次請求下載同一個(gè)文件時(shí),只需要下載第 500 個(gè)字節(jié)至第 999 個(gè)字節(jié)的數(shù)據(jù)就可以了。原理看上去很簡單,但我們需要考慮下面幾個(gè)問題:

1.是不是所有的 web 服務(wù)器都支持 Range 頭?

2.多次請求之間可能會(huì)間隔很長的時(shí)間,服務(wù)器上的文件發(fā)生了變化怎么辦?

3.如何保存下載的部分?jǐn)?shù)據(jù)和相關(guān)信息?

4.當(dāng)我們通過字節(jié)操作把一個(gè)文件拼成原始大小后,如何驗(yàn)證它和源文件一模一樣?

下面我們就帶著這些問題去探究斷點(diǎn)續(xù)傳的一些細(xì)節(jié)。

檢查服務(wù)器端對斷點(diǎn)續(xù)傳的支持

在服務(wù)器響應(yīng)我們的請求時(shí),會(huì)在響應(yīng)頭中通過 Accept-Ranges 指明是否接受請求一個(gè)資源的一部分?jǐn)?shù)據(jù)。但這里似乎有個(gè)小小的陷阱,就是不同的服務(wù)器可能返回不同的值來指明自己能夠接受部分資源的請求。貌似比較統(tǒng)一的方法是,當(dāng)服務(wù)器不支持請求部分?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ì)過一段時(shí)間再下載,也可能永遠(yuǎn)不會(huì)再接著下載了…
這里的問題是,當(dāng)下次要接著下載時(shí),如何確定服務(wù)器上的文件還是當(dāng)初下載了一半的那個(gè)文件。如果服務(wù)器上的文件已經(jīng)更新了,那無論如何都需要重新從頭開始下載。只有在服務(wù)器上的文件沒有發(fā)生變化的情況下,斷點(diǎn)續(xù)傳才有意義。
對于這個(gè)問題,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)

簡單點(diǎn)說 ETag 就是一個(gè)標(biāo)識(shí)當(dāng)前請求內(nèi)容的字符串,當(dāng)請求的資源發(fā)生變化后,對應(yīng)的 ETag 也會(huì)變化。好了,最簡單的辦法是第一次請求時(shí),把響應(yīng)頭中的 ETag 存下來,下次請求時(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沒有變化,可以斷點(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;
}

再來看看 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 就是所請求的資源在服務(wù)器上的最后一次修改時(shí)間。使用方法和 ETag 大體相同。

個(gè)人感覺使用 ETag 和 Last-Modified 中的任何一個(gè)都能達(dá)到我們的目的。但是你也可以兩個(gè)都用,做 double check,誰知道web服務(wù)器的實(shí)現(xiàn)是不是嚴(yán)格遵循了 HTTP 協(xié)議!

保存中間結(jié)果

這里主要就是用 C# 進(jìn)行文件操作。大體思路是如果有未下載完的文件,就把新下載的字節(jié)添加到文件的末尾,不再啰嗦,有興趣的同學(xué)請直接看 demo 代碼。

驗(yàn)證文件

在斷點(diǎn)續(xù)傳的過程中,我們以 byte 為單位下載、合并文件,如果整個(gè)過程中稍有沒有處理好的異常,可能最后得到的文件就和源文件不太一樣。因此最好是能夠?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ù)器都提供這樣的功能呢!

Demo 下載

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • C#算法之散列表

    C#算法之散列表

    本文詳細(xì)講解了C#算法之散列表,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-04-04
  • C#之泛型詳解

    C#之泛型詳解

    本文詳細(xì)講解了C#中的泛型,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-04-04
  • C#靜態(tài)static的用法實(shí)例分析

    C#靜態(tài)static的用法實(shí)例分析

    這篇文章主要介紹了C#靜態(tài)static的用法,以實(shí)例形式較為深入的分析了靜態(tài)類、靜態(tài)變量、靜態(tài)方法以及靜態(tài)構(gòu)造函數(shù)的特性與用法,需要的朋友可以參考下
    2014-10-10
  • C#中Byte[]和String之間轉(zhuǎn)換的方法

    C#中Byte[]和String之間轉(zhuǎn)換的方法

    很多朋友不清楚如何在Byte[]和String之間進(jìn)行轉(zhuǎn)換?下面小編給大家?guī)砹薭yte與string轉(zhuǎn)換的方法,感興趣的朋友參考下吧
    2016-08-08
  • C#中如何正確的使用字符串String

    C#中如何正確的使用字符串String

    這篇文章主要給大家介紹了關(guān)于在C#中如何正確的使用字符串String的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-12-12
  • C#調(diào)用python腳本的方法詳解

    C#調(diào)用python腳本的方法詳解

    這篇文章主要為大家詳細(xì)介紹了C#調(diào)用python腳本的方法,文中通過示例代碼介紹的非常詳細(xì),感興趣的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-11-11
  • 如何利用Jenkins + TFS為.Net Core實(shí)現(xiàn)持續(xù)集成/部署詳解

    如何利用Jenkins + TFS為.Net Core實(shí)現(xiàn)持續(xù)集成/部署詳解

    這篇文章主要給大家介紹了關(guān)于如何利用Jenkins + TFS為.Net Core實(shí)現(xiàn)持續(xù)集成/部署的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-05-05
  • C#反射之基礎(chǔ)應(yīng)用實(shí)例總結(jié)

    C#反射之基礎(chǔ)應(yīng)用實(shí)例總結(jié)

    這篇文章主要介紹了C#反射之基礎(chǔ)應(yīng)用實(shí)例總結(jié),包括了反射的基本原理與用法實(shí)例,需要的朋友可以參考下
    2014-10-10
  • C#函數(shù)式程序設(shè)計(jì)之用閉包封裝數(shù)據(jù)的實(shí)現(xiàn)代碼

    C#函數(shù)式程序設(shè)計(jì)之用閉包封裝數(shù)據(jù)的實(shí)現(xiàn)代碼

    如果一個(gè)程序設(shè)計(jì)語言能夠用高階函數(shù)解決問題,則意味著數(shù)據(jù)作用域問題已十分突出。當(dāng)函數(shù)可以當(dāng)成參數(shù)和返回值在函數(shù)之間進(jìn)行傳遞時(shí),編譯器利用閉包擴(kuò)展變量的作用域,以保證隨時(shí)能得到所需要的數(shù)據(jù)
    2014-03-03
  • 深入理解C#中foreach遍歷的使用方法

    深入理解C#中foreach遍歷的使用方法

    在c#中通過foreach遍歷一個(gè)列表是經(jīng)常拿用的方法,使用起來也方便,下面這篇文章先給大家介紹了關(guān)于C#中foreach遍歷的使用方法,后面介紹了c#使用foreach注意的一些是,文中通過示例代碼介紹的非常詳細(xì),對大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。
    2017-08-08

最新評論