前言
SpringBoot使用MultiPartFile接收來自表單的file文件,然后進行服務器的上傳是一個項目最基本的需求,我以前的項目都是基於SpringMVC框架搭建的,所以在使用SpringBoot的時候進行MultiPartFile上傳遇到了坑
遇到的問題
由於我需要對文件進行MIME-TYPE安全校驗,所以代碼中先后兩次調用了MultipartFile的transferTo()方法,在第二次調用的時候報錯,提示無法找到.tmp文件(事實上我在本地windows10環境下,兩次都沒報錯,可以正常上傳,是在服務器上運行時發現第二次調用報錯了,我查了一下,具體原因沒找到,可能和linux的臨時文件回收策略有關),以下是報錯信息:
java.io.FileNotFoundException: /home/app/sxpservice/temp/upload_e46ffe57_8ee6_4353_b533_d57c040bbc60_00000000.tmp (No such file or directory)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at org.apache.tomcat.util.http.fileupload.disk.DiskFileItem.getInputStream(DiskFileItem.java:188)
at org.apache.catalina.core.ApplicationPart.getInputStream(ApplicationPart.java:100)
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile.getBytes(StandardMultipartHttpServletRequest.java:292)
at com.suixingpay.fin.pcc.controller.UploadController.in(UploadController.java:54)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:644)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
注意:這里的報錯是提示一個后綴名為.tmp的臨時文件找不到,而不是.png之類的(和你上傳的文件相同格式)找不到,如果是后者,那么你遇到的是另一個問題,是location設置問題,具體解決方法可以百度,這里我們就不講了
問題分析
我們看一下transferTo()的實現方式可以了解到,其實就是對文件流進行了讀取,我們應該知道,流只可以被讀取一次,所以第二次讀取的時候肯定就報錯了,所以我考慮不直接讀取流,而是將流復制一份,這時候我想到將第一次調用改為使用FileUtils.copyInputStreamToFile()方法
問題解決
具體使用方式如下:
//獲取上傳的文件名
String fileName = uploadFile.getOriginalFilename();
// 獲取文件后綴名
String suffix=fileName.substring(fileName.lastIndexOf("."));
// 用uuid作為文件名,防止生成的臨時文件重復
File dest = File.createTempFile(UuidUtils.getUuid(), suffix);
// 將上傳文件復制到臨時文件
FileUtils.copyInputStreamToFile(uploadFile.getInputStream(),dest);
這樣就實現了文件流的復制,進而實現文件上傳,並且一個流可以使用多次
第一次寫博客,有不足和錯誤的地方,歡迎斧正,勿噴~~感謝感謝~