javascript
Spring MVC上传文件原理和resolveLazily说明
問題:使用Spring MVC上傳大文件,發(fā)現(xiàn)從頁面提交,到進入后臺controller,時間很長。懷疑是文件上傳完成后,才進入。由于在HTTP首部自定義了“Token”字段用于權(quán)限校驗,Token的有效時間很短,因此上傳大文件時,就會驗證Token失敗。
示例代碼:
前端:
<form action="upload" enctype="multipart/form-data" method="post"><table><tr><td>文件描述:</td><td><input type="text" name="description"></td></tr><tr><td>請選擇文件:</td><td><input type="file" name="file"></td></tr><tr><td><input type="submit" value="上傳"></td></tr></table> </form>?
controller:
@RequestMapping(value="/upload",method=RequestMethod.POST) public String upload(HttpServletRequest request, @RequestParam("description") String description, @RequestParam("file") MultipartFile file) throws Exception {System.out.println("enter controller."); // 文件上傳完才打印}springmvc-config.xml配置:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize"> <value>524288000</value> </property> <property name="defaultEncoding"><value>UTF-8</value></property></bean>Spring MVC上傳文件使用了Commons FileUpload類庫,即CommonsMultipartResolver使用commons Fileupload來處理 multipart請求,將Commons FileUpload的對象轉(zhuǎn)換成了Spring MVC的對象。
那如果直接使用Commons FileUpload來進行文件上傳下載呢?
示例代碼:
protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {System.out.println("enter servlet"); // 頁面提交后,立馬打印// 省略獲取上傳文件的邏輯
// ...
}
發(fā)現(xiàn)從頁面提交,很快就進入了servlet。
既然Spring也是使用了Commons FileUpload類庫,但為什么差別這么大呢?Spring在轉(zhuǎn)換過程中做了什么其他操作呢?
查看源碼,或者直接看([Java] SpringMVC工作原理之四:MultipartResolver)
發(fā)現(xiàn)有個resolveLazily參數(shù)是判斷是否要延遲解析文件(通過XML可以設(shè)置)。當?resolveLazily為false(默認)時,會立即調(diào)用 parseRequest() 方法對請求數(shù)據(jù)進行解析,然后將解析結(jié)果封裝到 DefaultMultipartHttpServletRequest中;而當resolveLazily為 true時,會在DefaultMultipartHttpServletRequest的initializeMultipart()方法調(diào)用parseRequest()方法對請求數(shù)據(jù)進行解析,而initializeMultipart()方法又是被getMultipartFiles()方法調(diào)用,即當需要獲取文件信息時才會去解析請求數(shù)據(jù),這種方式用了懶加載的思想。
再次修改代碼:
1、在springmvc-config.xml增加一個配置resolveLazily,設(shè)置true(如何配置見文末);
2、將controller方法中將@RequestParam("file") MultipartFile file注釋
?
再次測試,發(fā)現(xiàn)還是不對。。增加打印日志后發(fā)現(xiàn):
@RequestMapping(value="/upload",method=RequestMethod.POST) public String upload(HttpServletRequest request, @RequestParam("description") String description /*@RequestParam("file") MultipartFile file*/) throws Exception {System.out.println("enter controller."); // 還是文件上傳完后,才打印System.out.println(request.getHeader("Accept"));System.out.println(request.getParam("description"));// 省略獲取上傳文件的邏輯}
再注釋@RequestParam("description") String description
@RequestMapping(value="/upload",method=RequestMethod.POST) public String upload(HttpServletRequest request/*@RequestParam("description") String description, *//*@RequestParam("file") MultipartFile file*/) throws Exception {System.out.println("enter controller."); // 頁面提交后,立刻打印System.out.println(request.getHeader("Accept")); // 幾乎也是立刻打印System.out.println(request.getParam("description")); // 文件上傳完,才打印// 省略獲取上傳文件的邏輯
}
這次,第1,2條日志很快打印,第3條日志仍是文件上傳完后才打印。簡單分析下,應(yīng)該是后臺一直在接收數(shù)據(jù),HTTP首部很快就能獲取到并解析出來(首部和正文之間有一個空行),但請求正文部分必須在請求數(shù)據(jù)完全接收后才能解析,因此線程一直阻塞直到數(shù)據(jù)接收完成才打印第3條日志。因此解決方案是,把controller函數(shù)中的@RequestParam("description") String description也注釋掉,這樣頁面提交后,會立即進入controller中。
?
總結(jié)下最終的解決方案:
1、springmvc-config.xml增加一項配置:
<property name="resolveLazily "><value>true</value> </property>2、在controller的方法參數(shù)中,不能附加請求參數(shù),應(yīng)在函數(shù)中自己解析參數(shù)。
?
轉(zhuǎn)載于:https://www.cnblogs.com/cyh706510441/p/8855320.html
與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的Spring MVC上传文件原理和resolveLazily说明的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【C#】list 去重(转载)
- 下一篇: SpringMVC中@RequestPa