asp.net下大文件上傳知識(shí)整理
一、先簡要介紹一下RFC1867(Form-based File Upload in HTML)標(biāo)準(zhǔn):
1.帶有文件提交功能的HTML表單
現(xiàn)有的HTML規(guī)范為INPUT元素的TYPE屬性定義了八種可能的值,分別是:CHECKBOX, HIDDEN, IMAGE, PASSWORD, RADIO, RESET, SUBMIT, TEXT. 另外,當(dāng)表單采用POST方式的時(shí)候,表單默認(rèn)的具有"application/x-www -form-urlencoded" 的ENCTYPE屬性。
RFC1867標(biāo)準(zhǔn)對(duì)HTML做出了兩處修改:
1)為INPUT元素的TYPE屬性增加了一個(gè)FILE選項(xiàng)。
2)INPUT標(biāo)記可以具有ACCEPT屬性,該屬性能夠指定可被上傳的文件類型或文件格式列表。
另外,本標(biāo)準(zhǔn)還定義了一種新的MIME類型:multipart/form-data,以及當(dāng)處理一個(gè)帶有ENCTYPE="multipart/form-data" 并且/或含有<INPUT type="file">的標(biāo)記的表單時(shí)所應(yīng)該采取的行為。
舉例來說,當(dāng)HTML表單作者想讓用戶能夠上傳一個(gè)或更多的文件時(shí),他可以這么寫:
<FORM ENCTYPE="multipart/form-data" ACTION="_URL_" METHOD=POST>
File to process:
<INPUT NAME="userfile1" TYPE="file">
<INPUT TYPE="submit" VALUE="Send File">
</FORM>
HTML DTD里所需要做出的改動(dòng)是為InputType實(shí)體增加一個(gè)選項(xiàng)。此外,我們也建議用一系列用逗號(hào)分隔的文件類型來作為INPUT標(biāo)記的ACCEPT屬性。
... (其他元素) ...
<!ENTITY % InputType "(TEXT | PASSWORD | CHECKBOX |
RADIO | SUBMIT | RESET |
IMAGE | HIDDEN | FILE )">
<!ELEMENT INPUT - 0 EMPTY>
<!ATTLIST INPUT
TYPE %InputType TEXT
NAME CDATA #IMPLIED -- required for all but submit and reset
VALUE CDATA #IMPLIED
SRC %URI #IMPLIED -- for image inputs --
CHECKED (CHECKED) #IMPLIED
SIZE CDATA #IMPLIED --like NUMBERS,
but delimited with comma, not space
MAXLENGTH NUMBER #IMPLIED
ALIGN (top|middle|bottom) #IMPLIED
ACCEPT CDATA #IMPLIED --list of content types
>
... (其他元素) ...
2.文件傳輸延遲
在某些情況下,在確實(shí)準(zhǔn)備接受數(shù)據(jù)前,服務(wù)器先對(duì)表單數(shù)據(jù)中的某些元素(比如說用戶名,賬號(hào)等)進(jìn)行驗(yàn)證是推薦的做法。但是,經(jīng)過一定的考慮后,我們認(rèn)為如果服務(wù)器想這樣做的話,最好是采用一系列的表單,并將前面所驗(yàn)證過的數(shù)據(jù)元素作為“隱藏”字段傳回給客戶端,或者是通過安排表單使那些需要驗(yàn)證的元素先顯示出來。這樣的話,那些需要做復(fù)雜的應(yīng)用的服務(wù)器可以自己維持事務(wù)處理的狀態(tài),而那些簡單的應(yīng)用的則可以實(shí)現(xiàn)得簡單些。
HTTP 協(xié)議可能需要知道整個(gè)事務(wù)處理中的內(nèi)容總長度。即使沒有明確要求,HTTP客戶端也應(yīng)該提供上傳的所有文件的內(nèi)容總長度,這樣一個(gè)繁忙的服務(wù)器就能夠判斷文件的內(nèi)容是否是過大以至于將不能完整地處理,從而返回一個(gè)錯(cuò)誤代碼并關(guān)閉該連接,而不用等到接受了所有的數(shù)據(jù)才進(jìn)行判斷。目前一些現(xiàn)有的CGI應(yīng)用對(duì)所有的POST事務(wù)都需要知道內(nèi)容總長度。
如果INPUT標(biāo)記含有一個(gè)MAXLENGTH屬性,客戶端可以將這個(gè)屬性值看作是服務(wù)器端所能夠接受的傳送文件的最大字節(jié)數(shù)。在這種情況下,服務(wù)器能夠在上傳開始前,提示客戶端在服務(wù)器上有多少空間可以用來進(jìn)行文件上傳。但是應(yīng)該引起注意的是,這僅僅是一個(gè)提示,在表單被創(chuàng)建后和文件上傳前,服務(wù)器的實(shí)際需求可能會(huì)發(fā)生改變。
在任何情況下,如果接受的文件過大的話,任何一個(gè)HTTP服務(wù)器都有可能在文件傳輸?shù)倪^程中中斷傳輸。
3.傳輸二進(jìn)制數(shù)據(jù)的其他解決辦法
有些人曾經(jīng)建議使用一種新的MIME類型"aggregate",比如說aggregate/mixed 或是content-transfer- encoding "包"來描述那些不確定長度的二進(jìn)制數(shù)據(jù),而不是靠分解為多個(gè)部分來表示。雖然我們并不反對(duì)這么做,但這需要增加額外的設(shè)計(jì)和標(biāo)準(zhǔn)化工作來讓大家接受并理解"aggregate"。 從另一方面來說,"分解為多部分"的機(jī)制工作得很好,能夠非常簡單的在客戶發(fā)送端和服務(wù)器接受端加以實(shí)現(xiàn),而且能像其他一些綜合處理二進(jìn)制數(shù)據(jù)的方式一樣高效率地工作。
4.例子
假設(shè)服務(wù)器段提供的是如下的HTML:
<FORM ACTION="http://server.dom/cgi/handle"
ENCTYPE="multipart/form-data"
METHOD=POST>
What is your name? <INPUT TYPE=TEXT NAME=submitter>
What files are you sending? <INPUT TYPE=FILE NAME=pics>
</FORM>
用戶在“姓名”字段里面填寫"Joe Blow",對(duì)問題'What files are you sending?',用戶選擇
了一個(gè)文本文件"file1.txt"。
客戶段可能發(fā)送回如下的數(shù)據(jù):
Content-type: multipart/form-data, boundary=AaB03x
--AaB03x
content-disposition: form-data; name="field1"
Joe Blow
--AaB03x
content-disposition: form-data; name="pics"; filename="file1.txt"
Content-Type: text/plain
... file1.txt 的內(nèi)容...
--AaB03x--
如果用戶同時(shí)還選擇了另一個(gè)圖片文件"file2.gif",那么客戶端可能發(fā)送的數(shù)據(jù)將是:
Content-type: multipart/form-data, boundary=AaB03x
--AaB03x
content-disposition: form-data; name="field1"
Joe Blow
--AaB03x
content-disposition: form-data; name="pics"
Content-type: multipart/mixed, boundary=BbC04y
--BbC04y
Content-disposition: attachment; filename="file1.txt"
Content-Type: text/plain
... file1.txt 的內(nèi)容...
--BbC04y
Content-disposition: attachment; filename="file2.gif"
Content-type: image/gif
Content-Transfer-Encoding: binary
... file2.gif的內(nèi)容...
--BbC04y--
--AaB03x--
二、利用RFC1867標(biāo)準(zhǔn)處理文件上傳的兩種方式:
1.一次性得到上傳的數(shù)據(jù),然后分析處理。
看了N多代碼之后發(fā)現(xiàn),目前無組件程序和一些COM組件都是使用Request.BinaryRead方法。一次性得到上傳的數(shù)據(jù),然后分析處理。這就是為什么上傳大文件很慢的原因了,IIS超時(shí)不說,就算幾百M(fèi)文件上去了,分析處理也得一陣子。
2.一邊接收文件,一邊寫硬盤。
了解了一下國外的商業(yè)組件,比較流行的有Power-Web,AspUpload,ActiveFile,ABCUpload, aspSmartUpload,SA-FileUp。其中比較優(yōu)秀的是ASPUPLOAD和SA-FILE,他們號(hào)稱可以處理2G的文件(SA- FILE EE版甚至沒有文件大小的限制),而且效率也是非常棒,難道編程語言的效率差這么多?查了一些資料,覺得他們都是直接操作文件流。這樣就不受文件大小的制約。但老外的東西也不是絕對(duì)完美,ASPUPLOAD處理大文件后,內(nèi)存占用情況驚人。1G左右都是稀松平常。至于SA-FILE雖然是好東西但是破解難尋。然后發(fā)現(xiàn)2款.NET上傳組件,Lion.Web.UpLoadModule和AspnetUpload也是操作文件流。但是上傳速度和 CPU占用率都不如老外的商業(yè)組件。
做了個(gè)測試,LAN內(nèi)傳1G的文件。ASPUPLOAD上傳速度平均是4.4M/s,CPU占用10 -15,內(nèi)存占用700M。SA-FILE也差不多這樣。而AspnetUpload最快也只有1.5M/s,平均是700K/s,CPU占用15- 39,測試環(huán)境: PIII800,256M內(nèi)存,100M LAN。我想AspnetUpload速度慢是可能因?yàn)橐贿吔邮瘴募?,一邊寫硬盤。資源占用低的代價(jià)就是降低傳輸速度。但也不得不佩服老外的程序,CPU占用如此之低.....
三、ASP.NET上傳文件遇到的問題
我們?cè)谟肁SP.NET上傳大文件時(shí)都遇到過這樣或那樣的問題。設(shè)置很大的maxRequestLength值并不能完全解決問題,因?yàn)锳SP.NET會(huì) block直到把整個(gè)文件載入內(nèi)存后,再加以處理。實(shí)際上,如果文件很大的話,我們經(jīng)常會(huì)見到Internet Explorer顯示 "The page cannot be displayed - Cannot find server or DNS Error",好像是怎么也 catch不了這個(gè)錯(cuò)誤。為什么?因?yàn)檫@是個(gè)client side錯(cuò)誤,server side端的Application_Error是處理不到的。
四、ASP.NET大文件上傳解決方案
解決的方法是利用隱含的HttpWorkerRequest,用它的GetPreloadedEntityBody 和 ReadEntityBody方法從IIS為ASP.NET建立的pipe里分塊讀取數(shù)據(jù)。Chris Hynes為我們提供了這樣的一個(gè)方案(用HttpModule),該方案除了允許你上傳大文件外,還能實(shí)時(shí)顯示上傳進(jìn)度。
Lion.Web.UpLoadModule和AspnetUpload 兩個(gè).NET組件都是利用的這個(gè)方案。
方案原理:
利用HttpHandler實(shí)現(xiàn)了類似于ISAPI Extention的功能,處理請(qǐng)求(Request)的信息和發(fā)送響應(yīng)(Response)。
方案要點(diǎn):
1. httpHandler or HttpModule
a.在asp.net進(jìn)程處理request請(qǐng)求之前截獲request對(duì)象
b.分塊讀取和寫入數(shù)據(jù)
c.實(shí)時(shí)跟蹤上傳進(jìn)度更新meta信息
2. 利用隱含的HttpWorkerRequest用它的GetPreloadedEntityBody 和 ReadEntityBody方法處理文件流
IServiceProvider provider = (IServiceProvider) HttpContext.Current;
HttpWorkerRequest wr = (HttpWorkerRequest) provider.GetService(typeof(HttpWorkerRequest));
byte[] bs = wr.GetPreloadedEntityBody();
....
if (!wr.IsEntireEntityBodyIsPreloaded())
{
int n = 1024;
byte[] bs2 = new byte[n];
while (wr.ReadEntityBody(bs2,n) >0)
{
.....
}
}
3. 自定義Multipart MIME 解析器
自動(dòng)截獲MIME分割符
將文件分塊寫如臨時(shí)文件
實(shí)時(shí)更新Appliaction 狀態(tài)(ReceivingData, Error, Complete)
相關(guān)文章
.NET截取指定長度漢字超出部分以"..."代替 實(shí)例分享
.NET截取指定長度漢字超出部分以"..."代替 實(shí)例分享,需要的朋友可以參考一下2013-06-06VS2010/VS2013項(xiàng)目創(chuàng)建 ADO.NET連接mysql/sql server詳細(xì)步驟
這篇文章主要介紹了VS2010/VS2013項(xiàng)目創(chuàng)建,及ADO.NET連接mysql/sql server詳細(xì)步驟,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10asp.net viewstate 回發(fā)機(jī)制
ASP.NET中,為了模擬Winform中的事件響應(yīng)機(jī)制,微軟的工程師真是煞費(fèi)苦心,發(fā)明了“回發(fā)”機(jī)制,使得編寫WEB頁面變得和Winform一樣簡單。2010-03-03repeater隔行換色與鼠標(biāo)停留在上面達(dá)到變色效果
鼠標(biāo)停留在上面是變成其他的顏色,很多新手朋友都想實(shí)現(xiàn)這種效果,可是無從下手,本文整理了一些解決技巧,感興趣的朋友可以參考下啊2013-01-01ASP.NET 返回隨機(jī)數(shù)實(shí)現(xiàn)代碼
ASP.NET返回隨機(jī)數(shù),需要的朋友可以參考下。2009-11-11WEB上調(diào)用HttpWebRequest奇怪問題的解決方法
WEB上調(diào)用HttpWebRequest奇怪問題的解決方法...2007-04-04asp.net Parameters.AddWithValue方法在SQL語句的 Where 字句中的用法
今天晚上看論壇,有人提問說,Parameters.AddWithValue方法在有些情況下不好使2009-01-01asp.net 仿騰訊微薄提示 還能輸入*個(gè)字符 的實(shí)現(xiàn)代碼
asp.net 仿騰訊微薄提示 還能輸入*個(gè)字符 的實(shí)現(xiàn)代碼,需要的朋友可以參考下。2011-10-10無法將函數(shù)定義與現(xiàn)有的聲明匹配 問題的解決辦法 分享
無法將函數(shù)定義與現(xiàn)有的聲明匹配 問題的解決辦法 分享,需要的朋友可以參考一下2013-05-05