程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> 如何在ASP.NET中跟蹤和恢復大文件下載

如何在ASP.NET中跟蹤和恢復大文件下載

編輯:關於C#
 

在Web應用程序中處理大文件下載的問題一直出了名的困難,因此對於大多數站點來說,如果用戶的下載被中斷了,它們只能說悲哀降臨到用戶的身上了。但是我們現在不必這樣了,因為你可以使自己的ASP.NET應用程序有能力支持可恢復(繼續)的大文件下載。使用本文提供的方法的時候,你可以跟蹤下載的過程,這樣你就可以處理動態建立的文件--而且要達到這個目標根本不需要舊式的ISAPI動態鏈接庫和非受控的(unmanaged)C++代碼。

  為客戶端提供從互聯網上下載文件的服務最容易了,對嗎?僅僅只需要把可下載的文件復制到你的Web應用程序目錄中,發布鏈接並讓IIS完成所有相關的工作。但是,文件服務不應該比脖子上的疼痛還要多(還要麻煩),你不希望整個世界都能訪問自己的數據,你不希望服務器被數百個靜態文件塞滿了,你甚至於希望下載臨時文件--只有當客戶端開始下載後的空閒時間才建立這些文件。

  不幸的是,使用IIS對下載請求的默認的響應是不可能達到這些效果的。因此在一般情況下,為了獲得對下載過程的控制權,開發者需要鏈接到一個定制的.aspx頁面,在這個頁面中它們檢查用戶憑證(credential)、建立可以下載的文件並使用下面的代碼把該文件推送給客戶端:


Response.WriteFile
Response.End()

  而這就是出現真正麻煩的地方。

  有什麼問題?

  WriteFile方法看起來非常完美,它使文件的二進制數據流向客戶端。但是直到最近我們才知道,WriteFile方法是一個出名的內存占用狂,它把整個文件載入服務器的RAM中來提供服務(實際上它甚至於會占用文件兩倍大小的空間)。對於大文件,這會引起服務內存問題,並且可能重復ASP.NET過程。但是在2004年6月微軟發布了一個補丁解決了這個問題。這個補丁現在是.NET Framework 1.1補丁包(SP1)的一部分。

  這個補丁引入了TransmitFile方法,它把一個磁盤文件讀入到較小的內存緩沖區之後就開始傳輸該文件。盡管這個方案解決了內存和循環的問題,但是它仍然不能令人滿意。你不能控制響應的生命周期。你無法知道下載是否正確地完成了,你沒有辦法知道下載是否被中斷了,並且(如果你建立了臨時文件)你也不知道是否應該、以及什麼時候可以刪除這些文件。更糟的是,如果下載的確失敗了,TransmitFile方法又從客戶端下次嘗試的文件頭部開始下載。

  其中一種可能的解決方案--實現後台智能傳輸服務(BITS)對於多數站點來說是不可行的,因為這會毀掉維持客戶端浏覽器和操作系統獨立性而作出的努力。

  令人滿意的解決方案的基礎還是來自微軟用於解決WriteFile引起的內存混亂問題的第一次嘗試(見知識庫文章812406)。那篇文章演示了智能的大塊數據下載過程,它從文件流中讀取數據。在服務器把字節塊發送給客戶端之前,它使用Response.IsClientConnected屬性檢查客戶端是否仍然保持著連接。如果仍然保持連接,它就繼續發送流字節,否則就停止,以防止服務器發送不必要的數據。
這就是我們采用的方法,特別是在下載臨時文件的時候。在IsClientConnected返回False的情況下,你就知道下載過程被中斷了,你應該保存文件;反之,當這個過程成功完成的時候,你就刪除臨時文件。此外,為了恢復中斷了的下載,你需要做的工作是從上次下載嘗試過程中客戶端連接失敗的文件點開始下載。

  HTTP協議和頭信息(Header)支持

  HTTP協議支持可以用於處理被中斷下載的頭信息。使用少量的HTTP頭信息,你可以增強自己的下載過程,使它完全遵循HTTP協議規范。這個規范與ranges一起提供恢復被中斷的下載所需要的一切信息。

  下面是它的工作方式。首先,如果服務器支持客戶端斷點續傳,它就在初始的響應中發送Accept-Ranges頭信息。服務器還發送一個實體標簽(entity tag)頭信息(ETag),它包含一個唯一的標識字符串。

  下面的代碼顯示了IIS發送給客戶端的用於響應一個初始下載請求的一些頭信息,它向客戶端傳遞了被請求的文件的詳細信息。


HTTP/1.1 200 OK
Connection: close
Date: Tue, 19 Oct 2004 15:11:23 GMT
Accept-Ranges: bytes
Last-Modified: Sun, 26 Sep 2004 15:52:45 GMT
ETag: "47febb2cfd76c41:2062"
Cache-Control: private
Content-Type: application/x-zip-compressed
Content-Length: 2844011

  在接收這些頭信息之後,如果下載被中斷了,IE浏覽器在後來的下載請求中會把Etag值和Range頭信息發送回服務器。下面的代碼顯示了嘗試恢復被中斷下載時IE發送給服務器的一些頭信息。


GET http://192.168.100.100/download.zip HTTP/1.0
Range: bytes=822603-
Unless-Modified-Since: Sun, 26 Sep 2004 15:52:45 GMT
If-Range: "47febb2cfd76c41:2062"

  這些頭信息表明IE緩存了IIS提供的實體標簽,並在If-Range頭信息中把它發送回服務器了,這是確保下載從准確相同的文件恢復的一種途徑。不幸的是,並非所有的浏覽器的工作方式都相同。客戶端發送的用於驗證文件的其它HTTP頭信息可能是If-Match、If-Unmodified-Since或者Unless-Modified-Since。很明顯,該規范對於客戶端軟件必須支持哪些頭信息,或者必須使用哪些頭信息沒有明確的規定。因此,有些客戶端根本就沒有使用頭信息,而IE只使用If-Range和Unless-Modified-Since。你最好用代碼檢查這些信息。采用這種方式的時候,你的應用程序可以在非常高的層次遵循HTTP規范,並可以使用多種浏覽器。Range頭信息指明了被請求的字節范圍--在例子中它是服務器應該恢復文件流的起始點。

  當IIS接收到恢復下載的請求類型時,它發回包含下面的頭信息的響應信息:


HTTP/1.1 206 Partial Content
Content-Range: bytes 822603-2844010/2844011
Accept-Ranges: bytes
Last-Modified: Sun, 26 Sep 2004 15:52:45 GMT
ETag: "47febb2cfd76c41:2062"
Cache-Control: private
Content-Type: application/x-zip-compressed
Content-Length: 2021408


  請注意上面的代碼與最初的下載請求的HTTP響應有點差別--恢復下載的請求是206而最初下載的請求是200。這表明通過線路傳遞進來的內容是部分文件。這一次Content-Range頭信息指出了被傳遞字節的精確數量和位置。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved