介紹ASP.NET應(yīng)用程序
在Web程序中上傳文件是很常見的需求。利用HTTP協(xié)議上傳文件的方式非常有限,最常見的莫過于使用元素進行上傳。這種上傳方式會將內(nèi)容使用multipart/form-data方案進行編碼,并將內(nèi)容POST到服務(wù)器端。使用 multipart/form-data編碼方式與默認的application/x-url-encoded編碼方式相比,在大數(shù)據(jù)量情況下效率要高很多。
使用上傳文件最大的優(yōu)勢在于編程方便,幾乎各種服務(wù)器端技術(shù)都對這種上傳方式做了良好的封裝,使得程序員能夠直觀地對客戶端上傳的文件進行處理。不過總體來說,這個協(xié)議并不適合做文件傳輸,解析數(shù)據(jù)流內(nèi)容的代價相對較高,并且沒有一些例如斷點續(xù)傳的機制來輔助,導(dǎo)致在上傳大文件時經(jīng)常會力不從心。
有朋友認為使用上傳文件最大的問題在于內(nèi)存占用太高,由于需要將整個文件載入內(nèi)存進行處理,導(dǎo)致如果用戶上傳文件太大,或者同時上傳的用戶太多,會造成服務(wù)器端內(nèi)存耗盡。這個觀點其實是錯誤的。對于某些服務(wù)器端的技術(shù),例如Spring Framework,或者早期ASP.NET 1.1時,為了供程序處理,都會將用戶上傳的內(nèi)容完全載入內(nèi)存,這的確會帶來問題。但是其實協(xié)議本身并沒有規(guī)定服務(wù)器端應(yīng)該使用何種方式來處理上傳的文件。例如在現(xiàn)在的ASP.NET 2.0中就已經(jīng)會在用戶上傳數(shù)據(jù)超過一定數(shù)量之后將其存在硬盤中的臨時文件中,而這點對于開發(fā)人員完全透明,也就是說,開發(fā)人員可以像以前一樣進行數(shù)據(jù)流的處理。
swfupload也是個開源組件,顧名思義是使用Flash進行上傳。不過對于swfupload來說,F(xiàn)lash的作用主要是“控制”,而不是“展示 ”,這無疑給了開發(fā)人員更大的靈活性。swfupload的實現(xiàn)方式自然是利用了FileReference和 FileReferenceList組件所提供的功能,通過Flash與JavaScript的交互能力,使得開發(fā)文件上傳功能變得非常優(yōu)雅和容易。有了 swfupload,開發(fā)人員可以使用JavaScript來實現(xiàn)各種顯示方式,開發(fā)像Flicker一樣酷酷的上傳界面也不再是非常困難的事情了。
swfupload是個客戶端組件,它對于服務(wù)器端來說完全透明,也就是說,服務(wù)器端只需要使用對待普通form的方式來處理即可。例如在 ASP.NET中我們可以使用Generic Handler來處理客戶端的文件上傳。如下,fileCollection變量即為客戶端Post至服務(wù)器端所有文件的集合,我們可以使用name或下標的方式來獲得其中的HttpPostedFile對象。
ASP.NET 2.0啟用硬盤臨時文件的閾值(threshold)是可配置的:
- <system.web>
- <httpRuntime
- maxRequestLength="Int32"
- requestLengthDiskThreshold="Int32" />
- system.web>
maxRequestLength自不必說,剛接觸ASP.NET的朋友總會發(fā)現(xiàn)上傳文件不能超過4M,這就是因為 maxRequestLength的大小默認為4096,這就限制著每個請求的大小不得超過4096KB。這么做的目的是為了保護應(yīng)用程序不受惡意請求的危害。當(dāng)請求超過maxRequestLength之后,ASP.NET處理程序?qū)⒉粫幚碓撜埱蟆_@里和ASP.NET拋出一個異常是不同的,這就是為什么如果用戶上傳文件太大,看到的并非是ASP.NET應(yīng)用程序中指定的錯誤頁面(或者默認的),因為ASP.NET還沒有對這個請求進行處理。 requestLengthDiskThreshold就是剛才所提到的閾值,其默認值為256,即一個請求內(nèi)容超過256KB時就會啟用硬盤作為緩存。這個閾值理論上和客戶端是否是在上傳內(nèi)容無關(guān),只要客戶端發(fā)來的請求大于這個值即可。因此,在ASP.NET 2.0中服務(wù)器的內(nèi)存不會因為客戶端的異常請求而耗盡。
既然Flash提供了文件上傳功能,Silverlight作為微軟主推的RIA技術(shù)也不會缺了這項功能。這篇文章源自Silverlight 2.0的Quick Starts,展示了如何使用Silverlight 2.0開發(fā)文件上傳的功能,感興趣的朋友可以一讀。
圍繞著ASP.NET中上傳文件這個話題也討論了不少了,還有什么沒有涉及到的嗎?個人認為其實至少還有一個非常重要問題是沒有討論過,那就是在處理上傳文件時占用ASP.NET處理線程的問題。眾所周知,ASP.NET處理請求時會用到線程池中的線程,當(dāng)線程池中的線程被用完之后沒有被處理的請求只能排隊了。因此增大ASP.NET應(yīng)用程序吞吐量的一個重要手段,就是為一些耗時的操作使用異步處理方式(事實上這一命題可以在大部分應(yīng)用中成立)。例如一個數(shù)據(jù)庫查詢操作需要3秒鐘,如果不使用異步操作,處理線程就會被阻塞,直至查詢完成。如果使用異步方式來執(zhí)行數(shù)據(jù)庫查詢,在這3秒鐘內(nèi)線程就可以用戶處理其他請求,當(dāng)異步操作結(jié)束之后,ASP.NET就會使用另一個線程來繼續(xù)處理這個請求。
上傳大文件也是一個長時間占用處理線程的工作,而且遺憾的是,這無法使用異步操作來完成(通過異步操作來釋放處理線程需要操作系統(tǒng)的支持,因此只有少量功能可以使用異步操作)。如果一個文件上傳需要3分鐘時間,那么在這3分鐘內(nèi)就會獨占一個處理線程,如果上傳文件的連接一多,就會大大影響應(yīng)用程序的性能——就像遭受了某種方式的DOS攻擊一樣。因此,即使使用了像NeatUpload和swfupload這樣的組件,也無法解決上傳連接過多造成可用線程減少的問題。要解決這個問題并不容易,以下是兩種思路(歡迎大家就此問題進行討論):
◆擴展IIS,使上傳文件或處理文件的過程不經(jīng)ASP.NET處理,以減少ASP.NET應(yīng)用程序線程的消耗。現(xiàn)在有了IIS 7,如果使用集成管道模式,應(yīng)該也可以使用托管代碼進行擴展。
◆使用額外的ASP.NET應(yīng)用程序處理文件上傳,以節(jié)省上傳文件的線程對原ASP.NET應(yīng)用程序線程的消耗。
【編輯推薦】