1. 程式人生 > >Web開發中的檔案上傳問題研究

Web開發中的檔案上傳問題研究

前因:因為web開發用到檔案上傳功能,搜尋到了Valums,感覺很不錯,特別是它在非IE瀏覽器下通過HTML5技術來支援多檔案上傳,支援拖拽功能等,不過在具體用中並不順利,Valums在FF下罷工,追究原因,原來跟我使用Aapche FIleUpload有關,再追究,對檔案上傳的原理有了新的認識。

1. 檔案上傳時的抓包

不說理論,先看下對一個基於WEB的檔案上傳的抓包。在IE7/8下(不要用firefox或者chrome),在http://valums.com/ajax-upload/頁面上,上傳一個檔案,用wireshark抓包,結果如下:

sshot-2

眾所周知,一個HTTP報文主要請求行、請求頭部、空行和請求資料四部分組成,而對於檔案上傳來說,對應的HTTP報文的關鍵設定有:

  • 請求行設定為POST方式
  • 請求頭部設定Content-Length,設定為要傳輸的檔案長度
  • 請求頭部設定Content-Type:  multipart/form-data;   boundary=---------------------------88739631214394723612117964652, 這個boundary是用來作為多個上傳檔案的分割的。
  • 請求資料部分,每個部分(包括檔案和form內的html標籤等)的結構大致如下:

 

之所以在這兒提到抓包,是因為Apache FileUpload庫就是為這種格式的報文設計的,它的ServletFileUpload.isMultipartContent()方法,還有它常規的fileItem的遍歷操作都是建立在報文合乎上面格式的前提下才能正常使用。之前我並不清楚,後來在客戶端使用Valums庫出錯的時候才慢慢注意到這些問題的。

2. 傳統的解決方案

傳統的web頁面上的檔案上傳是通過html標籤來實現的:

 

當你在頁面上提交這樣的form時,瀏覽器器會為你自動生成1.中那樣格式的報文。本方式最大的缺點是提交就要重新整理頁面,於是,見下面。。。

3. 非同步提交的檔案上傳

因為ajax技術的引進,人們也希望檔案上傳做成非同步的,在頁面後臺進行,而不用重新整理整個頁面。gmail,QQ等紛紛採用flash技術,不過我還是想用純粹的js來解決這個問題。根據http://valums.com/ajax-upload/的思路,方法有兩種,一是在IE中使用隱藏的ifram+加非同步form提交,二是在FF或者Chrome中利用xhr + html5特性。

iframe+form非同步提交很簡單,在網頁動態增加一個iframe,在iframe裡動態增加一個帶有屬性enctype="multipart/form-data"的form,然後非同步提交這個form即可。

第二種方法,主要是利用XmlHttpRequest(簡稱XHR)技術來完成,具體實現上,可以利用Chrome等提供的FormData或者FileAPI,也可以只簡單地使用XHR來完成(這種方法有缺陷,當後臺使用Apache的Commons upload庫時,就會發現)。

4. Valums外掛的問題與解決方案

XHR是Ajax的核心類。我們可以用它來進行非同步通訊,包括非同步傳送檔案。Valums在Firefox和Chrome上關於檔案上傳的實現很簡單,核心程式碼如下:

 

不過,這種作法有個問題,當後臺使用Apache FileUpload庫的時候出現。在1.中已經提到過Valums在IE下通過IFRAME非同步上傳檔案時的抓包;現在對Valums在Chrome下進行抓包,發現包的資料部分不一樣,

sshot-1

很明顯,這個包與前面在IE下的抓包有兩處的關鍵不同,一是Content-Type沒有關於檔案上傳的設定;二是請求資料部分的組成不同。這樣的結果導致在後臺用Apache FileUpload處理檔案的時候無法識別(FileUpload的用法見參考4)。解決辦法有兩種,一種是在客戶端設定非同步請求的Content-Type,並在後臺避開FileUpload的常規用法,而直接用操作request.getInputStream,這樣的話,既要修改客戶端,又要在後臺判斷不同瀏覽器然後作出不同應對,不合算;我用另一種方法解決,在客戶端對Valums修改,使之與IE下通過IFRAME的請求報文完全相同,這樣就不用再改後臺了。

具體的作法是利用Chrome和FF所支援的HTML5新特性——FormData。FormData可以生成如1.中提到那樣傳統的檔案上傳請求格式,並且你可以自由地在FormData中通過append方法來增加各種key/value對作為引數,附加到請求中。

 

不過,這種解決方案的前提是瀏覽器支援FormData,IE不要指望了,FF和chrome比較新的應該都支援,具體的版本號我沒測試過,反正我機子上的瀏覽器都可以。

5. 參考:

1. http://hi.baidu.com/netpet/blog/item/423007faa0867f9058ee90c4.html

2. valums檔案上傳js外掛:http://valums.com/ajax-upload/

3. 關於File object: https://developer.mozilla.org/en/Using_files_from_web_applications

4. Apache Fileupload文件:http://commons.apache.org/fileupload/using.html