SpringBoot项目报错:The temporary upload location [/tmp/tomcat.4565031138659477751.9888/work/Tomcat/localhost/${项目名}] is not valid

在项目中碰到了一个报错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request; nested exception is java.io.IOException: The temporary upload location [/tmp/tomcat.4565031138659477751.9888/work
/Tomcat/localhost/${项目名}] is not valid
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.handleParseFailure(StandardMultipartHttpServletRequest.java:122)
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:113)
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.<init>(StandardMultipartHttpServletRequest.java:86)
at org.springframework.web.multipart.support.StandardServletMultipartResolver.resolveMultipart(StandardServletMultipartResolver.java:91)
at org.springframework.web.servlet.DispatcherServlet.checkMultipart(DispatcherServlet.java:1128)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:960)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:877)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)

......

原因

SpringBoot项目启动后会在/tmp目录下生成一个目录:tomcat.************.8080,其中结尾的8080是项目tomcat的端口号,使用Multipart(form-data)的方式上传文件时,会在这个目录下面创建临时文件。

由于Linux系统会定期对tmp下的文件夹进行清除,文件夹在长时间(默认10天)没有使用的情况下,就会被系统自动删除掉。/tmp目录的清理规则主要取决于/usr/lib/tmpfiles.d/tmp.conf文件的设定,默认的配置内容为:

1
2
3
# 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天前的目录和文件

根据上面的分析,如果上传文件的功能长时间没有使用,导致临时目录被删掉,则会抛出前面的那个异常了。

解决办法

方案一

直接重启项目, 会重新生成一个上面所说的临时文件夹。

方案二

在配置文件中配置tomcat的临时目录:

1
server.tomcat.basedir=/home/temp

该目录是用来存放Tomcat的日志、Dump等文件的临时文件夹,默认没有指定则会使用/tmp目录。

如果server.tomcat.basedir指定的目录不存在,项目启动的时候会自动创建。

方案三

在配置文件中配置multipart文件上传的临时目录:

1
spring.servlet.multipart.location=/home/temp

spring.servlet.multipart.location这个配置在旧版本的SpringBoot里是叫spring.http.multipart.location

对于multipart文件上传的临时目录,这个设置的优先级要高于server.tomcat.basedir,即如果spring.servlet.multipart.location有配置则以spring.servlet.multipart.location配置的目录为准,没有配置则会取server.tomcat.basedir配置的目录。

注意:spring.servlet.multipart.location配置的目录需要手动创建。

方案四

使用注解的方式配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Configuration
public class MultipartConfig {


/**
* 文件上传配置
* <p>
* 配置项:临时路径
*/
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory configFactory = new MultipartConfigFactory();
String path = System.getProperty("user.dir") + "/tmp/multipart";
File file = new File(path);
if (!file.exists()) {
file.mkdirs();
}
configFactory.setLocation(path);
return configFactory.createMultipartConfig();
}

}

这个和方案三的原理是一样的,不同之处是不需要手动去创建文件夹,因为代码里加了一个判断。

Servlet3.0后spring上传文件的机制

Spring 在处理上传文件的时候,会将当前文件的大小跟 fileSizeThreshold(默认为0)这个值比较,如果大于fileSizeThreshold则将文件保存在临时文件夹中,否则直接将文件放入内存中。 所以在上传文件过程中会对临时路径进行验证,如果临时路径不是文件夹或者不存在都将抛出异常。location临时路径可以通过spring.servlet.multipart.location指定。当不指定的时候会默认取servlet上下文临时存储目录:javax.servlet.context.tempdir,当servlet上下文临时存储目录不指定时会被赋值为tomcat的基本工作目录,tomcat基本工作目录可以由server.tomcat.basedir指定(linux 系统默认为在tmp目录下)。

------ 本文完 ------