• Spring Boot 文件上传与下载


    文件上传

    上传到服务器的磁盘路径

    @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));
            // 返回前端的文件访问地址(一般是http请求的地址,不要直接返回remorePath这样的磁盘路径)
            return remorePath; 
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    文件上传,前端需设置请求头为: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));
            // 返回前端的文件访问地址(一般是http请求的地址,不要直接返回remorePath这样的磁盘路径)
            return remorePath;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    其他参数的获取和普通的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();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    注: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;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    说明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
    
    • 1
    • 2
    • 3
    • 4

    常见问题解析

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102

    由于我们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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    使用 CommonsMultipartResolver

    1. 添加依赖
    <dependency>
        <groupId>commons-fileuploadgroupId>
        <artifactId>commons-fileuploadartifactId>
        <version>1.4version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 配置 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();
            // 完整请求的最大允许大小(参数单位是字节,-1标识不限制,默认-1)
            multipartResolver.setMaxUploadSize(multipartProperties.getMaxRequestSize().toBytes());
            // 单个上传文件的最大允许大小(参数单位是字节,-1标识不限制,默认-1)
            multipartResolver.setMaxUploadSizePerFile(multipartProperties.getMaxFileSize().toBytes());
            multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
            return multipartResolver;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    重用了MultipartProperties 的几个配置。

    注意:使用此方式也会有 MaxUploadSizeExceededException 无法通过ExceptionHandle 处理的问题。建议使用默认的 -1 不限制大小。然后在Controller 层添加过滤器或者直接在Controller中限制上传文件大小。

  • 相关阅读:
    全志R128芯片RTOS调试指南
    Shell基础— 变量定义的规则和分类
    05-流式操作:使用 Flux 和 Mono 构建响应式数据流
    【数据聚类】第八章第二节:谱聚类算法之切图聚类、算法流程及其实现
    预编码ZF,MMSE,THP准则线性预编码误码率仿真
    Intellij IDEA--常用的配置
    Web前端学习笔记6(transform-style,flex)
    P1091 [NOIP2004 提高组] 合唱队形
    [GXYCTF2019]Ping Ping Ping - RCE(空格、关键字绕过[3种方式])
    微软独家付费功能,也被完美解锁了
  • 原文地址:https://blog.csdn.net/forlinkext/article/details/127669437