0

    填坑之Multipart文件上传临时文件找不到路径

    2023.11.30 | admin | 66次围观

    填坑之Multipart文件上传临时文件找不到路径

    异常描述

    春节过后,生产报了个异常:

    主要是这句:The temporary upload location …… is not valid !临时的上传路径不可用,在服务器上找了一下,这个路径确实不存在,应该是文件丢失了

    处理异常 异常原因

    SpringBoot项目启动后,系统默认会在 /tmp 目录下自动创建如下三个目录:

     hsperfdata_root,
     tomcat.************.8080,(结尾是项目的端后)
     tomcat-docbase.*********.8080
     
     注: Multipart(form-data)的方式处理请求时,默认就是在第二个目录下创建临时文件的
    

    CentOS7会定期自动清理/tmp路径下的文件,清理规则如下:

    
    [eccore@vm035vmt006 /]$ cat /usr/lib/tmpfiles.d/tmp.conf
    #  This file is part of systemd.
    #
    #  systemd is free software; you can redistribute it and/or modify it
    #  under the terms of the GNU Lesser General Public License as published by
    #  the Free Software Foundation; either version 2.1 of the License, or
    #  (at your option) any later version.
    # See tmpfiles.d(5) for details
    # Clear tmp directories separately, to make them easier to override
    v /tmp 1777 root root 10d 		 # 清理/tmp下10天前的目录和文件
    v /var/tmp 1777 root root 30d	 # 清理/var/tmp下30天前的目录和文件
    # Exclude namespace mountpoints created with PrivateTmp=yes
    # 以下为排出清理的文件
    x /tmp/systemd-private-%b-*
    X /tmp/systemd-private-%b-*/tmp
    x /var/tmp/systemd-private-%b-*
    X /var/tmp/systemd-private-%b-*/tmp
    

    原因找到了,系统清理缓存导致,故要么限制系统清理缓存策略文件删除时找不到路径,要么将临时文件上传路径变更到其他路径即可!

    异常处理 修改清理策略

    不推荐!多台服务器均需修改,而且会遗留很多垃圾文件!

    简单暴力,直接修改系统清理策略,在文件末尾添加排出此文件夹:

    填坑之Multipart文件上传临时文件找不到路径

    x /tmp/tomcat.*
    

    启动脚本指定

    不推荐!启动命令不方便维护,如果使用自动化部署可以考虑!

    DIR=/home/Genterator
    FILETMPDIR=${DIR}/FILETMPDIR
    nohup java -Xms2048m -Xmx2048m -jar genterator.1.0.0-1.jar --spring.profiles.active=test --server.port=9820 -java.tmp.dir=$FILETMPDIR 2>1&
    

    配置文件指定

    推荐!

    在properties或者yml中指定:spring.servlet.multipart.location,如下:

    spring.servlet.multipart.max-file-size = 20MB
    spring.servlet.multipart.max-request-size=100MB
    spring.servlet.multipart.location=/home/eccore/tmp
    

    经测试,Linux是可以的文件删除时找不到路径,将缓存文件放入指定路径,但Windows作为开发环境存在异常(可能是我的环境导致,未验证):

    [2023-01-31 14:15:43.309] [traceid:e4cdabdc06fb1d5c] [spanId:e4cdabdc06fb1d5c] [http-nio-8084-exec-1] [ERROR] [cn.yto.exceptions.ExceptionCatch:261] -- >>> url:/webApi/finance/ordinaryBill/autoReconciliation, case:Failed to parse multipart servlet request; nested exception is java.io.IOException: The temporary upload location [C:\Users\yto\AppData\Local\Temp\tomcat.6215628767972368797.8084\work\Tomcat\localhost\webApi\home\eccore\tmp] is not valid
    org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request; nested exception is java.io.IOException: The temporary upload location [C:\Users\yto\AppData\Local\Temp\tomcat.6215628767972368797.8084\work\Tomcat\localhost\webApi\home\eccore\tmp] is not valid
    	at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.handleParseFailure(StandardMultipartHttpServletRequest.java:123)
    	at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:114)
    	at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.<init>(StandardMultipartHttpServletRequest.java:87)
    	at org.springframework.web.multipart.support.StandardServletMultipartResolver.resolveMultipart(StandardServletMultipartResolver.java:87)
    	at org.springframework.web.servlet.DispatcherServlet.checkMultipart(DispatcherServlet.java:1175)
    	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1010)
    	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
    	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
    	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908)
    	at javax.servlet.http.HttpServlet.service(HttpServlet.java:660)
    	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
    	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
    	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:53)
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    	at brave.servlet.TracingFilter.doFilter(TracingFilter.java:65)
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    	at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:123)
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    	…………
    

    Spring注解指定

    强烈推荐!

    通过SpringBoot的注解@Configuration指定配置类,将对应Bean(MultipartConfigElement)对象注入容器中,替换原来的Bean, 新注入的Bean中指定缓存存放路径!

    package cn.yto.steward.config;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.web.servlet.MultipartConfigFactory;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import javax.servlet.MultipartConfigElement;
    import java.io.File;
    /**
     * 文件上传配置
     *
     * @author YTO
     * @date 2023年01月31日 13:43
     */
    @Slf4j
    @Configuration
    public class MultipartConfig {
        /**
         * 文件上传临时路径
         */
        @Bean
        MultipartConfigElement multipartConfigElement() {
            MultipartConfigFactory factory = new MultipartConfigFactory();
            String location = System.getProperty("user.home") + File.separator + "tmp";
            log.info("文件上传临时路径:{}", location);
            File tmpFile = new File(location);
            if (!tmpFile.exists()) {
                boolean mkdirs = tmpFile.mkdirs();
                log.info("创建文件上传临时路径:{}, 创建结果:{}", location, mkdirs);
            }
            factory.setLocation(location);
            return factory.createMultipartConfig();
        }
    }
    

    版权声明

    本文仅代表作者观点。
    本文系作者授权发表,未经许可,不得转载。

    发表评论