美文网首页SpringFrameworkSpring cloud
Spring MVC: 大文件上传之 ERR_CONNECTIO

Spring MVC: 大文件上传之 ERR_CONNECTIO

作者: justZero | 来源:发表于2019-01-10 10:50 被阅读2次

    前言

    文件上传功能,想必大家都实现过,但最近自己却踩到一个不怎么被人注意的坑:上传文件过大导致浏览器出现 ERR_CONNECTION_RESET

    回顾

    回想一下 Spring MVC 配置文件上传的过程:

    @Configuration
    @EnableWebMvc
    @ComponentScan("your.controller")
    public class WebConfig extends WebMvcConfigurerAdapter {
        // 在该类中配置文件上传解析器的 Bean 交给 Spring 管理即可
        @Bean
        public CommonsMultipartResolver multipartResolver() throws IOException {
            CommonsMultipartResolver  resolver = new CommonsMultipartResolver();
            resolver.setDefaultEncoding("utf-8");
            resolver.setUploadTempDir(
                    new FileSystemResource(uploadTempDir));
            resolver.setMaxInMemorySize(0); 
            resolver.setMaxUploadSizePerFile(5*1024*1024L); 
            resolver.setMaxUploadSize(10*1024*1024L);
            return resolver;
        }
    }
    

    踩坑

    问题就出在这两个参数的设置上:

    resolver.setMaxUploadSizePerFile(5*1024*1024L); 
    resolver.setMaxUploadSize(10*1024*1024L);
    

    第一行,我们把上传中单个文件的最大值限制为 5MB
    第二行,我们把上传数据总大小的最大值限制为 10MB
    即如下情况均是过大:

    • 5MB + 5MB + 1MB (上传3个文件,总量过大)
    • 6MB + 4MB(上传2个文件,单文件过大)

    我们理想的处理逻辑思路有两种:

    1. 当上传数据超过这里的限制时会抛出相应的异常,这时我们使用 Spring MVC 的异常处理机制对异常进行处理并跳转到错误提示页面。
    2. 使用拦截器拦截上传请求,发现上传数据过大后,做进一步的处理。

    但是......实际效果却是大相径庭!


    只要上传数据大小超过这两个参数的任何一个,浏览器就会出现 ERR_CONNECTION_RESET,表示连接已被重置。一开始我认为是拦截器没有起作用,但经过多次测试,才发现只要上传数据过大,服务器程序(Tomcat、Jetty etc.)直接断开连接,请求根本到不了拦截器,连处理异常的机会都不给你。这次是服务器程序要背锅了。

    解决方案

    既然如此,那就来者不拒!我们把这两个参数设置的尽可能大,把处理逻辑交给拦截器和异常处理器,或者使用参考链接里给出的方案——配置相应服务器程序的属性,如 Tomcat 则需要配置 Connector 中的 maxSwallowSize ,默认只有 2MB。详情请阅读:Tomcat 8 - HTTP Connector

    resolver.setMaxUploadSizePerFile(666*1024*1024*1024L); // 666GB
    resolver.setMaxUploadSize(666*1024*1024*1024L); // 666GB
    

    拦截器

    @Slf4j
    @Component
    @PropertySource("classpath:app.properties")
    public class FileUploadInterceptor implements HandlerInterceptor {
        // 使用 SpEL 表达式解析,避免类型转换异常
        @Value("#{T(Long).parseLong('${upload.maxsize}')}")
        private Long maxSize;
    
        @Override
        public boolean preHandle(
                HttpServletRequest request,
                HttpServletResponse response,
                Object handler) {
            if (request != null &&
                    ServletFileUpload.isMultipartContent(request)) {
                ServletRequestContext requestContext = new ServletRequestContext(request);
                long requestSize = requestContext.contentLength();
                log.info("上传数据大小:{}MB", requestSize/1024/1024);
                if (requestSize > maxSize) {
                    throw new MaxUploadSizeExceededException(maxSize);
                }
            }
            return true;
        }
    
        @Override
        public void postHandle(
                HttpServletRequest request,
                HttpServletResponse response,
                Object handler,
                ModelAndView modelAndView) throws Exception {
        }
    
        @Override
        public void afterCompletion(
                HttpServletRequest request,
                HttpServletResponse response,
                Object handler, Exception ex) throws Exception {
        }
    }
    

    异常处理

    @Slf4j
    @ControllerAdvice
    public class AppExceptionHandler {
        @ExceptionHandler(MaxUploadSizeExceededException.class)
        public String handleUploadOverSize(
                MaxUploadSizeExceededException e,
                Model model) {
            log.error(e.getMessage());
            model.addAttribute("message", "上传失败!文件太大了!!!");
            return "home";
        }
    }
    

    tips: 大家也许注意到代码里 log 实例根本没有被创建就直接使用了?这是 Lombok@slf4j 注解的功劳。这是一个代码简化工具包,十分推荐大家了解并使用。

    参考

    [1] Could not upload large file to server by using Spring MVC

    相关文章

      网友评论

        本文标题:Spring MVC: 大文件上传之 ERR_CONNECTIO

        本文链接:https://www.haomeiwen.com/subject/tdsorqtx.html