前言
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);
这样就实现了文件流的复制,进而实现文件上传,并且一个流可以使用多次
第一次写博客,有不足和错误的地方,欢迎斧正,勿喷~~感谢感谢~