WebWork文件上傳問題解析
今天遇到一個(gè)webwork文件上傳的問題,上傳 txt 文件的時(shí)候,如果文件內(nèi)容為空,則上傳后的文件為null,上傳不成功。試了一下,如果給txt文件寫一個(gè)二進(jìn)制的0x00字符,即可以上傳成功,如果是上傳一個(gè)沒有內(nèi)容的word文檔,也可以成功,分析原因應(yīng)該是word格式自帶了很多隱藏格式數(shù)據(jù),所以其實(shí)沒有文字內(nèi)容的word文檔也是有東西的。這樣看來,只要文件實(shí)際內(nèi)容為空,即不帶任何字符,上傳就有問題。
隨后,我查看了一下webwork.properties文件,發(fā)現(xiàn)我們項(xiàng)目的webwork.multipart.parser選用的是jakarta,
于是我依次換用了另外兩種parser:pell和cos。最后發(fā)現(xiàn),只有cos不會(huì)出錯(cuò),能夠上傳成功??磥恚蔷唧wparser對(duì)上傳文件流的解析不一樣。
三種parser都是市面上早已存在的,webwork只不過是對(duì)它們進(jìn)行了封裝調(diào)用,并不是靠自己來實(shí)現(xiàn)的。webwork提供了一個(gè)通用的訪問接口MultiPartRequest,然后針對(duì)三種parser,分別繼承實(shí)現(xiàn)了CosMultiPartRequest、PellMultiPartRequest、JakartaMultiPartRequest。默認(rèn)情況下,如果你不在webwork.properties文件中設(shè)置parser,webwork會(huì)選擇pell作為parser。
三種parser的區(qū)別是:只有Jakarta能做多文件的同時(shí)上傳;
只有pell能自動(dòng)支持中文名文件的上傳,其他兩種需要你自己手動(dòng)做encoding;cos功能比較強(qiáng)大,比如我上面提到的txt文件內(nèi)容為空,cos可以上傳成功,但其他兩種parser就不行,不過webwork的封裝使它喪失了很多功能。
另外,webwork.properties文件里還有其他幾個(gè)與文件上傳相關(guān)的參數(shù),比如webwork.multipart.saveDir用于設(shè)定上傳文件的臨時(shí)文件保存目錄,webwork.multipart.maxSize用于設(shè)置上傳文件的最大字節(jié)數(shù)。
我又上網(wǎng)找了一篇文章,深入介紹了WebWork文件上傳的機(jī)制和過程,還找了一篇剖析webwork源碼的pdf《Anatomy Webwork Source Code》,大家可以去下載看看 http://public.iecn.net/Along/Anatomy%20Webwork%20Source%20Code_V0.9.pdf
那篇深入介紹Webwork文件上傳機(jī)制的文章(http://www.wangchao.net.cn/bbsdetail_267965.html)如下:
點(diǎn)擊上傳按鈕后,webwork的程序流如下:
- step1)進(jìn)入ServletDispatcher.service
- publicvoidservice
- (HttpServletRequestrequest,HttpServletResponseresponse)
- throwsServletException{
- ........
- request=wrapRequest(request);
- .........
- }
- step2)進(jìn)入ServletDispatcher.wrapRequest
- protectedHttpServletRequestwrapRequest
- (HttpServletRequestrequest)throwsIOException{
- ........................
- if(MultiPartRequest.isMultiPart(request)){
- request=newMultiPartRequestWrapper
- (request,getSaveDir(),getMaxSize());
- }
- returnrequest;
- }
- step3)進(jìn)入MultiPartRequestWrapper的構(gòu)造方法
- 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)獲取后通過反射實(shí)例化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 進(jìn)入JakartaMultiPartRequest的構(gòu)造方法(我在webwork配置的parser是Jakarta所以進(jìn)入了這個(gè)方法,如果你配置不同的parser回進(jìn)入不同的parser
- public JakartaMultiPartRequest
- (HttpServletRequest servletRequest,
- String saveDir, int maxSize)
- throws IOException {
- //設(shè)置保存參數(shù)
- 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 {
//此方法生成文件,將請(qǐng)求中的每個(gè)參數(shù)都生成一個(gè)臨時(shí)文件比如upload_00000017.tmp, upload_00000018.tmp等,就算是form提交的參數(shù)也如此
- List items = upload.parseRequest(servletRequest);
- ......................
- }
執(zhí)行完第四步,然后推出ServletDispatcher.wrapRequest,進(jìn)入serviceAction方法,開始action及其攔截器的棧調(diào)用
進(jìn)入action和調(diào)用棧后,攔截器或action可通過如下代碼訪問上傳的臨時(shí)文件
- MultiPartRequestWrapper wrapper =
- (MultiPartRequestWrapper) req;
- File doc = wrapper.getFiles("doc")[0];
從上面的分析可以看出:
1)WebWork文件上傳在進(jìn)入action棧之前不修改源碼或者做一些擴(kuò)展、覆蓋之類的動(dòng)作,在進(jìn)入action棧的時(shí)候文件已經(jīng)上傳,而且其文件名很難跟蹤(upload_00000017.tmp,到底是00000017,0000018,或者0000022等等),畢竟有很多人上傳文件,所以臨時(shí)文件名很難確定,所以如果你想知道上傳的進(jìn)度很難。
2)利用WebWork文件上傳是兩次拷貝過程,webwork首先從request的輸入流中將文件流輸出到一個(gè)臨時(shí)文件,然后你再將此臨時(shí)文件拷貝到你需要指定的路徑。
【編輯推薦】