WebWork文件上傳問題解析
今天遇到一個webwork文件上傳的問題,上傳 txt 文件的時候,如果文件內容為空,則上傳后的文件為null,上傳不成功。試了一下,如果給txt文件寫一個二進制的0x00字符,即可以上傳成功,如果是上傳一個沒有內容的word文檔,也可以成功,分析原因應該是word格式自帶了很多隱藏格式數據,所以其實沒有文字內容的word文檔也是有東西的。這樣看來,只要文件實際內容為空,即不帶任何字符,上傳就有問題。
隨后,我查看了一下webwork.properties文件,發現我們項目的webwork.multipart.parser選用的是jakarta,
于是我依次換用了另外兩種parser:pell和cos。最后發現,只有cos不會出錯,能夠上傳成功。看來,是具體parser對上傳文件流的解析不一樣。
三種parser都是市面上早已存在的,webwork只不過是對它們進行了封裝調用,并不是靠自己來實現的。webwork提供了一個通用的訪問接口MultiPartRequest,然后針對三種parser,分別繼承實現了CosMultiPartRequest、PellMultiPartRequest、JakartaMultiPartRequest。默認情況下,如果你不在webwork.properties文件中設置parser,webwork會選擇pell作為parser。
三種parser的區別是:只有Jakarta能做多文件的同時上傳;
只有pell能自動支持中文名文件的上傳,其他兩種需要你自己手動做encoding;cos功能比較強大,比如我上面提到的txt文件內容為空,cos可以上傳成功,但其他兩種parser就不行,不過webwork的封裝使它喪失了很多功能。
另外,webwork.properties文件里還有其他幾個與文件上傳相關的參數,比如webwork.multipart.saveDir用于設定上傳文件的臨時文件保存目錄,webwork.multipart.maxSize用于設置上傳文件的最大字節數。
我又上網找了一篇文章,深入介紹了WebWork文件上傳的機制和過程,還找了一篇剖析webwork源碼的pdf《Anatomy Webwork Source Code》,大家可以去下載看看 http://public.iecn.net/Along/Anatomy%20Webwork%20Source%20Code_V0.9.pdf
那篇深入介紹Webwork文件上傳機制的文章(http://www.wangchao.net.cn/bbsdetail_267965.html)如下:
點擊上傳按鈕后,webwork的程序流如下:
- step1)進入ServletDispatcher.service
- publicvoidservice
- (HttpServletRequestrequest,HttpServletResponseresponse)
- throwsServletException{
- ........
- request=wrapRequest(request);
- .........
- }
- step2)進入ServletDispatcher.wrapRequest
- protectedHttpServletRequestwrapRequest
- (HttpServletRequestrequest)throwsIOException{
- ........................
- if(MultiPartRequest.isMultiPart(request)){
- request=newMultiPartRequestWrapper
- (request,getSaveDir(),getMaxSize());
- }
- returnrequest;
- }
- step3)進入MultiPartRequestWrapper的構造方法
- publicMultiPartRequestWrapper
- (HttpServletRequestrequest,StringsaveDir,intmaxSize)
- throwsIOException{
- .....................
- //step3.1)獲取webwork.preperties配置的parser
- Stringparser="";
- parser=Configuration.getString("webwork.multipart.parser");
- //Ifit'snotset,usePell
- if(parser.equals("")){
- log.warn("Propertywebwork.multipart.parsernotset."+
- "Usingcom.opensymphony.webwork.dispatcher.multipart.
- PellMultiPartRequest");
- parser="com.opensymphony.webwork.dispatcher.
- multipart.PellMultiPartRequest";
- }
- //legacysupportforoldstylepropertyvalues
- elseif(parser.equals("pell")){
- parser="com.opensymphony.webwork.dispatcher.
- multipart.PellMultiPartRequest";
- }elseif(parser.equals("cos")){
- parser="com.opensymphony.webwork.dispatcher.
- multipart.CosMultiPartRequest";
- }elseif(parser.equals("jakarta")){
- parser="com.opensymphony.webwork.dispatcher.
- multipart.JakartaMultiPartRequest";
- }
- //step3.2)獲取后通過反射實例化parser
- try{
- ClassbaseClazz=com.opensymphony.webwork.dispatcher.
- multipart.MultiPartRequest.class;
- Classclazz=Class.forName(parser);
- //makesureitextendsMultiPartRequest
- if(!baseClazz.isAssignableFrom(clazz)){
- addError("Class'"+parser+"'doesnotextendMultiPartRequest");
- return;
- }
- //gettheconstructor
- Constructorctor=clazz.getDeclaredConstructor(newClass[]{
- Class.forName("javax.servlet.http.HttpServletRequest"),
- java.lang.String.class,int.class
- });
- //buildtheparameterlist
- Object[]parms=newObject[]{
- request,saveDir,newInteger(maxSize)
- };
- //instantiateit
- multi=(MultiPartRequest)ctor.newInstance(parms);
- .................................................
- }
//step4 進入JakartaMultiPartRequest的構造方法(我在webwork配置的parser是Jakarta所以進入了這個方法,如果你配置不同的parser回進入不同的parser
- public JakartaMultiPartRequest
- (HttpServletRequest servletRequest,
- String saveDir, int maxSize)
- throws IOException {
- //設置保存參數
- DiskFileUpload upload = new DiskFileUpload();
- // we must store all uploads on disk because
- the ww multipart API is missing streaming
- // capabilities
- upload.setSizeThreshold(0);
- upload.setSizeMax(maxSize);
- if (saveDir != null) {
- upload.setRepositoryPath(saveDir);
- }
- // Parse the request
- try {
//此方法生成文件,將請求中的每個參數都生成一個臨時文件比如upload_00000017.tmp, upload_00000018.tmp等,就算是form提交的參數也如此
- List items = upload.parseRequest(servletRequest);
- ......................
- }
執行完第四步,然后推出ServletDispatcher.wrapRequest,進入serviceAction方法,開始action及其攔截器的棧調用
進入action和調用棧后,攔截器或action可通過如下代碼訪問上傳的臨時文件
- MultiPartRequestWrapper wrapper =
- (MultiPartRequestWrapper) req;
- File doc = wrapper.getFiles("doc")[0];
從上面的分析可以看出:
1)WebWork文件上傳在進入action棧之前不修改源碼或者做一些擴展、覆蓋之類的動作,在進入action棧的時候文件已經上傳,而且其文件名很難跟蹤(upload_00000017.tmp,到底是00000017,0000018,或者0000022等等),畢竟有很多人上傳文件,所以臨時文件名很難確定,所以如果你想知道上傳的進度很難。
2)利用WebWork文件上傳是兩次拷貝過程,webwork首先從request的輸入流中將文件流輸出到一個臨時文件,然后你再將此臨時文件拷貝到你需要指定的路徑。
【編輯推薦】