文件上传
上传到服务器的磁盘路径
@RestController
public class UpDownloadController{
@RequestMapping("uploadFile")
public String uploadFile(MultipartFile file) throws IOException {
String remorePath = "/home/test";
String fileName = file.getOriginalFilename();
String filePath = remorePath+"/"+fileName;
file.transferTo(new File(filePath));
return remorePath;
}
}
文件上传,前端需设置请求头为:multipart/form-data。
MultipartFile常用方法说明
String getName():获取文件对应的表单参数名称
String getOriginalFilename():获取文件的名称
InputStream getInputStream():获取文件的输入流。我们的示例是上传到服务器的磁盘,很多项目可能是上传到文件服务器等。可通过该输入流将文件数据对接到文件服务器的对应接口上。
byte[] getBytes():获取文件的数据
void transferTo(File dest):将文件输出到定义的 dest 文件
带其他参数的文件上传
@RequestMapping("uploadFile")
public String uploadFile(Param param,MultipartFile file) throws IOException {
log.error("文件上传参数:{}",param);
String remorePath = "/home/test";
String fileName = file.getOriginalFilename();
String filePath = remorePath+"/"+fileName;
file.transferTo(new File(filePath));
return remorePath;
}
其他参数的获取和普通的Controller获取一样。
文件下载
@RequestMapping("downloadFile/{param1}/{param2}")
public void downloadFile(@PathVariable Long param1, @PathVariable Long param2,String filePath, HttpServletResponse response) throws IOException {
File f = new File("C:\\work\\test\\2022_11_01\\720513e0d488.txt");
log.error("param1:{},param2:{},filePath:{}",param1,param2,filePath);
String fileName = new String("测试下载".getBytes("UTF-8"),"ISO-8859-1") + ".txt";
response.setContentType("application/force-download");
response.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
FileUtils.copyFile(f,response.getOutputStream());
response.getOutputStream().close();
}
注:fileName 如果直接使用汉字字符串,下载后文件名称变成了下划线。通过示例中的方式转换即可。
支持文件上传下载的配置说明
如果在 Spring MVC 下,需要配置 一个名称为 multipartResolver 的 MultipartResolver 类型的bean来支持文件上传。Spring-web包为其提供了两个实现CommonsMultipartResolver、StandardServletMultipartResolver
类名 | 说明 |
---|
CommonsMultipartResolver | 基于 Commons FileUpload 上仅适用于POST请求,但接受任何多部分/内容类型。 | StandardServletMultipartResolver | 使用Servlet容器的多部分解析器,可能会使应用程序暴露于容器实现的差异。 |
如果要使用 CommonsMultipartResolver 需要引入 Commons FileUpload 依赖包。
spring boot 下,默认是可以不用什么配置,就支持文件的上传和下载的。我们来看看spring boot 的相关自动配置代码
@Bean(name = "multipartResolver")
@ConditionalOnMissingBean(MultipartResolver.class)
public StandardServletMultipartResolver multipartResolver() {
StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
return multipartResolver;
}
说明Spring Boot 默认是使用的 StandardServletMultipartResolver。但其使用了条件注解,我们可以在我们的应用中使用 CommonsMultipartResolver 将其替换掉。
限制上传文件的大小
Spring Boot 自动配置中还有个类我们需要了解 MultipartProperties,文件上传的配置。其在application.properties 文件中的配置前缀为 spring.servlet.multipart
- location:指定上载文件的存储目录。如果未指定,将使用临时目录。
- max-file-size :指定上传文件允许的最大大小。默认值为1MB(意味着我们上传文件的默认最大大小为1MB)
- max-request-size:指定多部分/表单数据请求允许的最大大小。默认值为10MB。
- file-size-threshold :文件大小阈值指定文件将写入磁盘的大小阈值。默认值为0。
- resolve-lazily:是否在文件或参数访问时延迟解析多部分请求
配置示例
# 上传文件的表单数据最大大小
spring.servlet.multipart.max-request-size= 20MB
# 上传文件的最大大小
spring.servlet.multipart.max-file-size= 5MB
常见问题解析
MaxUploadSizeExceededException 异常 @ControllerAdvice 标注的 ExceptionHandler 无法解析的问题
org.springframework.web.multipart.MaxUploadSizeExceededException: Maximum upload size exceeded; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException: the request was rejected because its size (336958720) exceeds the configured maximum (20971520)
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.handleParseFailure(StandardMultipartHttpServletRequest.java:122)
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:115)
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.initializeMultipart(StandardMultipartHttpServletRequest.java:129)
at org.springframework.web.multipart.support.AbstractMultipartHttpServletRequest.getMultipartFiles(AbstractMultipartHttpServletRequest.java:141)
at org.springframework.web.multipart.support.AbstractMultipartHttpServletRequest.getFile(AbstractMultipartHttpServletRequest.java:88)
at org.springframework.web.multipart.support.MultipartResolutionDelegate.resolveMultipartArgument(MultipartResolutionDelegate.java:108)
at org.springframework.web.method.annotation.RequestParamMethodArgumentResolver.resolveName(RequestParamMethodArgumentResolver.java:166)
at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:108)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:179)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:146)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:681)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:61)
at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108)
at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137)
at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)
at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108)
at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137)
at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)
at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:450)
at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365)
at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)
at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)
at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:387)
at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362)
at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at com.yyoo.mybase.base.filter.CorsFilter.doFilter(CorsFilter.java:58)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at com.yyoo.mybase.base.filter.xss.XssFilter.doFilter(XssFilter.java:75)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:142)
at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:82)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1732)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException: the request was rejected because its size (336958720) exceeds the configured maximum (20971520)
at org.apache.catalina.connector.Request.parseParts(Request.java:2974)
at org.apache.catalina.connector.Request.getParts(Request.java:2834)
at org.apache.catalina.connector.RequestFacade.getParts(RequestFacade.java:1098)
at javax.servlet.http.HttpServletRequestWrapper.getParts(HttpServletRequestWrapper.java:361)
at javax.servlet.http.HttpServletRequestWrapper.getParts(HttpServletRequestWrapper.java:361)
at javax.servlet.http.HttpServletRequestWrapper.getParts(HttpServletRequestWrapper.java:361)
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:95)
... 81 common frames omitted
Caused by: org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException: the request was rejected because its size (336958720) exceeds the configured maximum (20971520)
at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.init(FileItemIteratorImpl.java:161)
at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.getMultiPartStream(FileItemIteratorImpl.java:205)
at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.findNextItem(FileItemIteratorImpl.java:224)
at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.<init>(FileItemIteratorImpl.java:142)
at org.apache.tomcat.util.http.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:252)
at org.apache.tomcat.util.http.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:276)
at org.apache.catalina.connector.Request.parseParts(Request.java:2932)
... 87 common frames omitted
由于我们Spring Boot 默认使用了容器的文件上传支持,所以此处的错误是 tomcat 容器报的,此处修改办法如下
修改配置文件
# 上传文件的表单数据最大大小
spring.servlet.multipart.max-request-size= 20MB
# 上传文件的最大大小
spring.servlet.multipart.max-file-size= 5MB
# 设置tomcat的允许大小为-1,不限制
server.tomcat.max-swallow-size = -1
# 是否在文件或参数访问时延迟解析多部分请求
spring.servlet.multipart.resolve-lazily=true
使用 CommonsMultipartResolver
- 添加依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
- 配置 CommonsMultipartResolver
@Configuration
public class MyWebConfig {
@Resource
private MultipartProperties multipartProperties;
@Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
@ConditionalOnMissingBean(MultipartResolver.class)
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(multipartProperties.getMaxRequestSize().toBytes());
multipartResolver.setMaxUploadSizePerFile(multipartProperties.getMaxFileSize().toBytes());
multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
return multipartResolver;
}
}
重用了MultipartProperties 的几个配置。
注意:使用此方式也会有 MaxUploadSizeExceededException 无法通过ExceptionHandle 处理的问题。建议使用默认的 -1 不限制大小。然后在Controller 层添加过滤器或者直接在Controller中限制上传文件大小。
|