1. 程式人生 > >原始碼剖析CommonsMultipartResolver解釋parseRequest無法獲得FileItem

原始碼剖析CommonsMultipartResolver解釋parseRequest無法獲得FileItem

相信在Javaweb做檔案上傳的時候大家都用到commons-fileupload這個元件,使用這個元件實現檔案上傳時都會用到這句程式碼List<FileItem> list = servletFileUpload.parseRequest(request);,意思是解析從客戶端傳送到伺服器的request請求(Form表單)得到一個FileItem的List集合,這樣每個FileItem都可以簡單地通過封裝好的方法獲得檔名,檔案資訊等。但是一旦處理不慎就會出現List.size()為0的問題,即解析後得不到FileItem。

琢磨了很久我才知道,之所以之前commons-fileupload能上傳但後來又不能上傳,是因為後來我在配置檔案中注入了一個叫做CommonsMultipartResolver的元件。注入這個元件是因為在專案其他地方要用到MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;

,只有注入了這個元件,request強制轉換為MultipartHttpServletRequest才不會報錯。

這樣說來,沒有引入CommonsMultipartResolver前,commons-fileupload通過servletFileUpload.parseRequest(request)將request解析了,能得到FileItem集合。引入CommonsMultipartResolver之後,進行同樣的解析得不到FileItem集合了。那只有一個原因,就是前後request物件變了。下面就來debug檢視前後request物件的區別:

沒有引入CommonsMultipartResolver前:

這裡寫圖片描述

引入CommonsMultipartResolver後:

這裡寫圖片描述

這裡寫圖片描述

對比前後可見,request物件確實不一樣。那CommonsMultipartResolver究竟做了什麼?

開啟CommonsMultipartResolver原始碼:

protected MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {
        String encoding = this.determineEncoding(request);
        FileUpload fileUpload = this
.prepareFileUpload(encoding); try { List ex = ((ServletFileUpload)fileUpload).parseRequest(request); return this.parseFileItems(ex, encoding); } catch (SizeLimitExceededException var5) { throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), var5); } catch (FileUploadException var6) { throw new MultipartException("Could not parse multipart servlet request", var6); } }

這裡寫圖片描述

從原始碼可以看到:CommonsMultipartResolver本身就依賴於commons-fileupload這個包,自己也有一個叫做parseRequest的方法,這個方法呼叫了commons-fileupload包裡FileUpload的parseRequest方法

現在知道原因了,原來CommonsMultipartResolver這個元件會檢測從客戶端來的request若是multipart/form-data資料就會自動將其用common-fileupload的parseRequest方法進行解析。因此引入這個元件後,想再通過List<FileItem> list = servletFileUpload.parseRequest(request); 解析得到FileItem是不可能的,此前已經解析過一次了,再解析當然為空

解決方案:

不要再使用List<FileItem> list = servletFileUpload.parseRequest(request); 這個方法得到FileItem的List(之後遍歷這個List獲取Form表單的資料)。
既然CommonsMultipartResolver已經幫我們解析了,那就直接用:

if(!ServletFileUpload.isMultipartContent(request)){
                //如果上傳的資料不是表單原始的資料(經過編碼),則直接返回。因為只有表單原始資料才會被接收
                //http://stackoverflow.com/questions/4526273/what-does-enctype-multipart-form-data-mean
                return;
            }

            MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
            dynamicsText = multipartRequest.getParameter("dynamicsText");//獲取表單文字部分
            MultipartFile multipartFile = multipartRequest.getFile("dynamicsFile");//獲取表單

            String savePath = request.getSession().getServletContext().getRealPath("/") + "user_space/"+userId;
            File file = new File(savePath);
            if (!file.exists() && !file.isDirectory()) {
                file.mkdirs();//新建資料夾(多重)
            }
            dynamicsFile = multipartFile.getOriginalFilename();
            multipartFile.transferTo(new File(savePath+"/"+dynamicsFile));

如有問題或補充,請大家在評論中儘管提,共同交流^~^。