ASP.NET中的HTTP協(xié)議
ASP.NET中的HTTP協(xié)議
在Web應(yīng)用程序中處理大文件下載的問題一直出了名的困難,因此對于大多數(shù)站點來說,如果用戶的下載被中斷了,它們只能說悲哀降臨到用戶的身上了。但是我們現(xiàn)在不必這樣了,因為你可以使自己的ASP.NET應(yīng)用程序有能力支持可恢復(fù)(繼續(xù))的大文件下載。使用本文提供的方法的時候,你可以跟蹤下載的過程,這樣你就可以處理動態(tài)建立的文件——而且要達(dá)到這個目標(biāo)根本不需要舊式的ISAPI動態(tài)鏈接庫和非受控的(unmanaged)C++代碼。
為客戶端提供從互聯(lián)網(wǎng)上下載文件的服務(wù)最容易了,對嗎?僅僅只需要把可下載的文件復(fù)制到你的Web應(yīng)用程序目錄中,發(fā)布鏈接并讓IIS完成所有相關(guān)的工作。但是,文件服務(wù)不應(yīng)該比脖子上的疼痛還要多(還要麻煩),你不希望整個世界都能訪問自己的數(shù)據(jù),你不希望服務(wù)器被數(shù)百個靜態(tài)文件塞滿了,你甚至于希望下載臨時文件——只有當(dāng)客戶端開始下載后的空閑時間才建立這些文件。
不幸的是,使用IIS對下載請求的默認(rèn)的響應(yīng)是不可能達(dá)到這些效果的。因此在一般情況下,為了獲得對下載過程的控制權(quán),開發(fā)者需要鏈接到一個定制的。 aspx頁面,在這個頁面中它們檢查用戶憑證(credential)、建立可以下載的文件并使用下面的代碼把該文件推送給客戶端:
Response.WriteFile
Response.End()
而這就是出現(xiàn)真正麻煩的地方。
有什么問題?
WriteFile方法看起來非常完美,它使文件的二進(jìn)制數(shù)據(jù)流向客戶端。但是直到最近我們才知道,WriteFile方法是一個出名的內(nèi)存占用狂,它把整個文件載入服務(wù)器的RAM中來提供服務(wù)(實際上它甚至于會占用文件兩倍大小的空間)。對于大文件,這會引起服務(wù)內(nèi)存問題,并且可能重復(fù)ASP.NET過程。但是在2004年6月微軟發(fā)布了一個補(bǔ)丁解決了這個問題。這個補(bǔ)丁現(xiàn)在是。NET Framework 1.1補(bǔ)丁包(SP1)的一部分。
這個補(bǔ)丁引入了TransmitFile方法,它把一個磁盤文件讀入到較小的內(nèi)存緩沖區(qū)之后就開始傳輸該文件。盡管這個方案解決了內(nèi)存和循環(huán)的問題,但是它仍然不能令人滿意。你不能控制響應(yīng)的生命周期。你無法知道下載是否正確地完成了,你沒有辦法知道下載是否被中斷了,并且(如果你建立了臨時文件)你也不知道是否應(yīng)該、以及什么時候可以刪除這些文件。更糟的是,如果下載的確失敗了,TransmitFile方法又從客戶端下次嘗試的文件頭部開始下載。
其中一種可能的解決方案——實現(xiàn)后臺智能傳輸服務(wù)(BITS)對于多數(shù)站點來說是不可行的,因為這會毀掉維持客戶端瀏覽器和操作系統(tǒng)獨立性而作出的努力。
令人滿意的解決方案的基礎(chǔ)還是來自微軟用于解決WriteFile引起的內(nèi)存混亂問題的第一次嘗試(見知識庫文章812406)。那篇文章演示了智能的大塊數(shù)據(jù)下載過程,它從文件流中讀取數(shù)據(jù)。在服務(wù)器把字節(jié)塊發(fā)送給客戶端之前,它使用Response.IsClientConnected屬性檢查客戶端是否仍然保持著連接。如果仍然保持連接,它就繼續(xù)發(fā)送流字節(jié),否則就停止,以防止服務(wù)器發(fā)送不必要的數(shù)據(jù)。
這就是我們采用的方法,特別是在下載臨時文件的時候。在IsClientConnected返回False的情況下,你就知道下載過程被中斷了,你應(yīng)該保存文件;反之,當(dāng)這個過程成功完成的時候,你就刪除臨時文件。此外,為了恢復(fù)中斷了的下載,你需要做的工作是從上次下載嘗試過程中客戶端連接失敗的文件點開始下載。
HTTP協(xié)議和頭信息(Header)支持
HTTP協(xié)議支持可以用于處理被中斷下載的頭信息。使用少量的HTTP頭信息,你可以增強(qiáng)自己的下載過程,使它完全遵循HTTP協(xié)議規(guī)范。這個規(guī)范與ranges一起提供恢復(fù)被中斷的下載所需要的一切信息。
【編輯推薦】