C# 檔案下載 : WinINet

來源:互聯網
上載者:User

標籤:檔案下載winnet

在 C# 中,除了 WebClient 我們還可以使用一組 WindowsAPI 來完成下載任務。這就是 Windows Internet,簡稱 WinINet。本文通過一個 demo 來介紹 WinINet 的基本用法和一些實用技巧。

介面介紹

相比 WebClient 的用法,Win32API 在使用時可能會煩瑣一些。所以先把用到的 API 簡單介紹一下。

資源的初始化和釋放

InternetOpen
這是需要調用的第一個方法,它會初始化內部資料結構,為後面的調用做準備。

InternetCloseHandle
這個方法用來關閉使用中開啟的 Internet 控制代碼,釋放資源。

建立到伺服器的串連

InternetOpenUrl
這是一個通用的函數,應用程式可以用它來請求資料(只要是 WinINet 支援的協議就可以)。尤其是當我們僅僅想要通過一個 URL 擷取資料,而不關心通訊協定相關的內容時,這個介面就特別合適。該方法會解析參數中的 URL 字串,然後建立到伺服器的串連,並準備下載由 RUL 標識的資料。

檢查響應資訊

HttpQueryInfo
檢索與 HTTP 要求相關的前序資訊。主要是查看請求是否成功。

讀取響應內容

InternetReadFile
從 InternetOpenUrl 開啟的控制代碼中讀取資料。

下載過程

這裡我們只介紹下載過程中的關鍵環節,完整的過程請參考本文的 demo。

InternetOpenUrl

當請求與伺服器建立串連時,我們重點考慮三個問題:請求的 url,是否使用 RELOAD 標識, 用戶端是否支援 gzip 壓縮。

請求的 url 不用多說,這裡直接請求一個 http url。

我們不希望拿到用戶端緩衝中的資料,所以希望每次請求都能夠從伺服器重新下載。此時需要為 InternetOpenUrl 方法傳入 INTERNET_FLAG_RELOAD 標識。
當前絕大多數的 網頁伺服器都是支援 gzip 壓縮的,我們的用戶端當然也要能夠解壓縮伺服器傳回來的 gzip 格式的資料。所以我們要在請求中告訴伺服器,用戶端是能夠處理 gzip 資料的。只有這樣,伺服器才會主動的返回 gzip 格式的資料。
代碼如下:

string referer = "Referer: xxxxxx\nAccept-Encoding: gzip";// INTERNET_FLAG_RELOAD -> 0x80000000// 跳過緩衝,強制從原始的伺服器下載資料 hInetFile = NativeMethods.InternetOpenUrl(this._hInet, uri.AbsoluteUri, referer, referer.Length, 0x80000000, IntPtr.Zero);
HttpQueryInfo

接下來我們開始檢查前面發送的請求返回的 header 中的資訊。主要是:請求的資源是否存在,返回的資料有多長,返回的檔案的原始名稱是什麼,返回的資料是以什麼格式被壓縮的。

我們先要通過檢查返回的狀態代碼來確定請求是否成功,也就是返回的是不是 200。

byte[] content = new byte[BufferSize];int count = BufferSize;int temp = 0;NativeMethods.HttpQueryInfo(hInetFile, 19, content, out count, out temp)statuscode = Encoding.Unicode.GetString(content, 0, count);

正確返回時,statuscode 應該是“200”。
不要對 HttpQueryInfo 的第二個參數感到奇怪,為了獲得請求的返回狀態我們就得傳入 19。你可以參考Query Onfo Flags 。
用類似的方法可以得到返回資料的長度,原始的檔案名稱,返回資料的格式。

InternetReadFile

前面一切順利的話就可以讀取資料了。這個方法本身沒什麼可說的,但出於簡化操作的目的,筆者對 InternetReadFile 進行了簡單的封裝。建立了一個繼承自 Stream 的類 MyInternetReadStream。在重寫的 Read 方法中調用 InternetReadFile,並且添加了一個回調方法用來計算下載進度等資訊。下面是代碼概要,完整代碼請參考 demo。

650) this.width=650;" src="/img/fz.gif" alt="複製代碼" style="margin:0px;padding:0px;border:none;" />

public override int Read(byte[] buffer, int offset, int count){    int dwNumberOfBytesToRead = Math.Min(BufferSize, count);    int length = 0;    NativeMethods.InternetReadFile(this._hInetFile, this._localBuffer, dwNumberOfBytesToRead, out length)    Array.Copy(this._localBuffer, 0, buffer, offset, length);    this._bytesReadCallback(length, this._contentLength);    return length;}

650) this.width=650;" src="/img/fz.gif" alt="複製代碼" style="margin:0px;padding:0px;border:none;" />

Gzip stream

前面我們提到,伺服器可能返回的是經過 gzip 壓縮的資料,這就需要我們先檢查資料的格式。如果是 gzip 格式的資料就需要把它解壓縮。其實這在 C# 中是很簡單的,我們只要把剛才建立的 MyInternetReadStream 的執行個體傳給 GZipStream 的建構函式,建立一個新的 GZipStream 執行個體就可以了。

650) this.width=650;" src="/img/fz.gif" alt="複製代碼" style="margin:0px;padding:0px;border:none;" />

private Stream GetInternetStream(IntPtr hInetFile){    //檢查資料是不是gzip格式    string contentEncoding = MyWinInet.GetContentEncoding(hInetFile);    if (contentEncoding.IndexOf("gzip", StringComparison.OrdinalIgnoreCase) != -1)    {        return new GZipStream(this.ForGZipReadStream(hInetFile), CompressionMode.Decompress, false);    }    …}private Stream ForGZipReadStream(IntPtr hInetFile){    return new MyWinInet.MyInternetReadStream(hInetFile, new MyWinInet.MyInternetReadStream.BytesReadCallback(this.BytesReadCallback));}

650) this.width=650;" src="/img/fz.gif" alt="複製代碼" style="margin:0px;padding:0px;border:none;" />

至於計算下載進度,即時的下載速度的實現和 《C# 檔案下載 : WebClient》中的實現基本相同,請參考上文,或者直接看本文的 demo。


C# 檔案下載 : WinINet

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.