SpringBoot上传找不到临时目录报错问题
一、问题描述
据线上用户反馈,上传文件功能突然报错。经过排查日志文件,报错信息为:
Failed to parse multipart servlet request; nested exception is java.lang.RuntimeException: java.nio.file.NoSuchFileException: /tmp/undertow.8099.1610110889708131476/undertow1171565914461190366upload
二、原因排查
原来在linux
操作系统中通过java -jar
启动Spring Boot
应用时会默认在/tmp
目录下创建临时目录(windows操作系统中为C:\Users\Default\AppData\Local\Temp
),临时目录一般为undertow.端口.*
这种格式(若是tomcat
容器,则为tomcat.端口.*
,本文将以undertow
举例,tomcat
同理),文件上传时需要先转换成临时文件存储在这里。但是/tmp
目录下的文件如果超过10天没有使用,就会被系统自动清理,所以在再次上传时由于父目录被删除出现上述问题。
三、问题重现
由于该临时目录会在服务启动时自动创建,因此在本地或测试环境重启服务,然后删除/tmp
下生成的undertow.端口.*
(若使用tomcat
,则为tomcat.端口.*
)目录并再次上传文件即可重现。
四、解决方案
1、手动创建该临时目录(不推荐)
mkdir -p /tmp/undertow.8099.1610110889708131476/undertow1171565914461190366upload
PS:若再次超过10天未上传文件,会出现同样的问题,治标不治本。
2、修改linux系统配置(不推荐)
vim /usr/lib/tmpfiles.d/tmp.con
# 在文件最后添加,表示不清理undertow开头的文件夹
x /tmp/undertow*
PS:若部署了多台服务器,则每台服务器都需要修改。
3、修改Spring Boot配置文件(推荐)
spring:
servlet:
multipart:
# 指定自定义上传目录
location: /mnt/tmp
PS:使用此方式必须保证/mnt/tmp
存在,若不存在则会出现同样的错误。因此需要在每次服务启动时判断,该目录存在则忽略,不存在则创建,代码如下:
@Slf4j
@Configuration
public class MultipartConfig {
@Value("${spring.servlet.multipart.location}")
private String fileTempDir;
@Bean
MultipartConfigElement multipartConfigElement() {
String os = System.getProperty("os.name");
// 兼容windows
if(os.toLowerCase().startsWith("win")){
fileTempDir = "C:" + fileTempDir;
}
log.info("fileTempDir:{}", fileTempDir);
MultipartConfigFactory factory = new MultipartConfigFactory();
File tmpDirFile = new File(fileTempDir);
// 判断文件夹是否存在
if (!tmpDirFile.exists()) {
//创建文件夹
boolean mkdirSuccess = tmpDirFile.mkdirs();
log.info("create temp dir,result:{}", mkdirSuccess);
}
factory.setLocation(fileTempDir);
return factory.createMultipartConfig();
}
}