Spring MVC: 大文件上傳之 ERR_CONNECTION_RESET

前言

文件上傳功能,想必大家都實(shí)現(xiàn)過(guò),但最近自己卻踩到一個(gè)不怎么被人注意的坑:上傳文件過(guò)大導(dǎo)致瀏覽器出現(xiàn) ERR_CONNECTION_RESET

回顧

回想一下 Spring MVC 配置文件上傳的過(guò)程:

@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;
    }
}

踩坑

問(wèn)題就出在這兩個(gè)參數(shù)的設(shè)置上:

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

第一行,我們把上傳中單個(gè)文件的最大值限制為 5MB;
第二行,我們把上傳數(shù)據(jù)總大小的最大值限制為 10MB。
即如下情況均是過(guò)大:

  • 5MB + 5MB + 1MB (上傳3個(gè)文件,總量過(guò)大)
  • 6MB + 4MB(上傳2個(gè)文件,單文件過(guò)大)

我們理想的處理邏輯思路有兩種:

  1. 當(dāng)上傳數(shù)據(jù)超過(guò)這里的限制時(shí)會(huì)拋出相應(yīng)的異常,這時(shí)我們使用 Spring MVC 的異常處理機(jī)制對(duì)異常進(jìn)行處理并跳轉(zhuǎn)到錯(cuò)誤提示頁(yè)面。
  2. 使用攔截器攔截上傳請(qǐng)求,發(fā)現(xiàn)上傳數(shù)據(jù)過(guò)大后,做進(jìn)一步的處理。

但是......實(shí)際效果卻是大相徑庭!


只要上傳數(shù)據(jù)大小超過(guò)這兩個(gè)參數(shù)的任何一個(gè),瀏覽器就會(huì)出現(xiàn) ERR_CONNECTION_RESET,表示連接已被重置。一開(kāi)始我認(rèn)為是攔截器沒(méi)有起作用,但經(jīng)過(guò)多次測(cè)試,才發(fā)現(xiàn)只要上傳數(shù)據(jù)過(guò)大,服務(wù)器程序(Tomcat、Jetty etc.)直接斷開(kāi)連接,請(qǐng)求根本到不了攔截器,連處理異常的機(jī)會(huì)都不給你。這次是服務(wù)器程序要背鍋了。

解決方案

既然如此,那就來(lái)者不拒!我們把這兩個(gè)參數(shù)設(shè)置的盡可能大,把處理邏輯交給攔截器和異常處理器,或者使用參考鏈接里給出的方案——配置相應(yīng)服務(wù)器程序的屬性,如 Tomcat 則需要配置 Connector 中的 maxSwallowSize ,默認(rèn)只有 2MB。詳情請(qǐng)閱讀: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 表達(dá)式解析,避免類型轉(zhuǎn)換異常
    @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("上傳數(shù)據(jù)大小:{}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 實(shí)例根本沒(méi)有被創(chuàng)建就直接使用了?這是 Lombok@slf4j 注解的功勞。這是一個(gè)代碼簡(jiǎn)化工具包,十分推薦大家了解并使用。

參考

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 蘇敏常對(duì)我說(shuō),生活就是個(gè)地雷區(qū),你真不知道哪天你哪只腳就踩上了那些地雷,問(wèn)題是現(xiàn)在我們還兩只腳踩上了。等哪天繃緊的...
    烏龍小桃桃閱讀 390評(píng)論 0 3
  • In 1858, a French engineer, Aime Thome de Gamond, suggest...
    化真閱讀 306評(píng)論 0 1
  • 梳理一下HTTP 狀態(tài)碼. 可以點(diǎn)擊W3C網(wǎng)頁(yè)獲取更多信息 五種狀態(tài)碼 1xx 消息 2xx 成功 3xx ...
    天之朗閱讀 697評(píng)論 0 0
  • 刷微博時(shí)看到一條,“男人喜歡你素顏不化妝,你瘦了他心疼,你胖了他高興,那個(gè)人只有你爸?!焙呛?,我輕笑。旁邊的...
    Greydeer貓烏閱讀 406評(píng)論 0 0
  • 南嶺國(guó)家森林公園位于韶關(guān)市乳源縣大橋鎮(zhèn),我是沖著廣東第一峰——石坑崆來(lái)南嶺國(guó)家森林公園的,結(jié)果由于山頂成了軍事禁區(qū)...
    大兵日記閱讀 1,421評(píng)論 2 3

友情鏈接更多精彩內(nèi)容