7月17
上传找不到临时目录, 出现500错误,nested exception is java.lang. RuntimeException: java.nio.file. NoSuchFileException: /tmp/undertow
Linux 系统中,Spring Boot 应用以 java -jar 命令启动时,会在操作系统的 /tmp 目录下生成一个 tomcat(或 undertow )临时目录,上传的文件先要转换成临时文件保存在这个文件夹下面。由于临时 /tmp 目录下的文件,在长时间(10天)没有使用的情况下,系统执行了 tmp 目录清理服务(systemd-tmpfiles-clean.service),导致 /tmp/undertow...8090 文件被清理,然而在上传的时候,undertow 服务器需要创建/tmp/undertow...8090/undertow...upload 临时文件,但是调用 Files.createFile(...) 的时候就会发现找不到父目录,才导致了以上的错误。
具体错误日志(参考)
undertow
java.nio.file.NoSuchFileException: /tmp/undertow.17753558642503713859.8085/undertow7370242804103803588upload
Tomcat
The temporary upload location [/tmp/tomcat.7957874575370093230.8088/work/Tomcat/localhost/ROOT] is not valid
重现方法
找到类 io.undertow.server.handlers.form.MultiPartParserDefinition
定位到如下代码
@Override
public void beginPart(final HeaderMap headers) {
this.currentFileSize = 0;
this.headers = headers;
final String disposition = headers.getFirst(Headers.CONTENT_DISPOSITION);
if (disposition != null) {
if (disposition.startsWith("form-data")) {
currentName = Headers.extractQuotedValueFromHeader(disposition, "name");
fileName = Headers.extractQuotedValueFromHeaderWithEncoding(disposition, "filename");
if (fileName != null && fileSizeThreshold == 0) {
try {
if (tempFileLocation != null) {
file = Files.createTempFile(tempFileLocation, "undertow", "upload");
} else {
file = Files.createTempFile("undertow", "upload");
}
createdFiles.add(file);
fileChannel = FileChannel.open(file, StandardOpenOption.READ, StandardOpenOption.WRITE);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
在 createdFiles.add(file); 处打断点,复制file的 path 的值找到该文件并将其删除;放开断点,错误重现;
解决方案
方案1 (推荐)
在 applicaiton.yml(applicaiton.property) 中添加配置 :spring.servlet.multipart.location
spring.servlet.multipart.location 底层就是 createMultipartConfig 中的 factory.setLocation ,见源码:org.springframework.boot.autoconfigure.web.servlet.MultipartProperties#createMultipartConfig
spring:
servlet:
multipart:
location: /data/tmp
手动指定目录后,必须保证该目录存在,并由读写的权限
创建该目录 mkdir -p /data/tmp
方法二
使用配置类配置, 类似方案1
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setLocation(System.getProperty("/data/tmp"));
return factory.createMultipartConfig();
}
方案三(不推荐)
手动创建临时目录
mkdir -p /tmp/undertow.17753558642503713859.8085/undertow7370242804103803588upload
方案四(不推荐)
修改系统配置,排除该临时目录
vim /usr/lib/tmpfiles.d/tmp.con
# 文件最后添加
x /tmp/undertow*
来源:Heck's Blog
地址:https://www.heckjj.com/post/651/
转载时须以链接形式注明作者和原始出处及本声明,否则将追究法律责任,谢谢配合!
具体错误日志(参考)
undertow
java.nio.file.NoSuchFileException: /tmp/undertow.17753558642503713859.8085/undertow7370242804103803588upload
Tomcat
The temporary upload location [/tmp/tomcat.7957874575370093230.8088/work/Tomcat/localhost/ROOT] is not valid
重现方法
找到类 io.undertow.server.handlers.form.MultiPartParserDefinition
定位到如下代码
@Override
public void beginPart(final HeaderMap headers) {
this.currentFileSize = 0;
this.headers = headers;
final String disposition = headers.getFirst(Headers.CONTENT_DISPOSITION);
if (disposition != null) {
if (disposition.startsWith("form-data")) {
currentName = Headers.extractQuotedValueFromHeader(disposition, "name");
fileName = Headers.extractQuotedValueFromHeaderWithEncoding(disposition, "filename");
if (fileName != null && fileSizeThreshold == 0) {
try {
if (tempFileLocation != null) {
file = Files.createTempFile(tempFileLocation, "undertow", "upload");
} else {
file = Files.createTempFile("undertow", "upload");
}
createdFiles.add(file);
fileChannel = FileChannel.open(file, StandardOpenOption.READ, StandardOpenOption.WRITE);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
在 createdFiles.add(file); 处打断点,复制file的 path 的值找到该文件并将其删除;放开断点,错误重现;
解决方案
方案1 (推荐)
在 applicaiton.yml(applicaiton.property) 中添加配置 :spring.servlet.multipart.location
spring.servlet.multipart.location 底层就是 createMultipartConfig 中的 factory.setLocation ,见源码:org.springframework.boot.autoconfigure.web.servlet.MultipartProperties#createMultipartConfig
spring:
servlet:
multipart:
location: /data/tmp
手动指定目录后,必须保证该目录存在,并由读写的权限
创建该目录 mkdir -p /data/tmp
方法二
使用配置类配置, 类似方案1
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setLocation(System.getProperty("/data/tmp"));
return factory.createMultipartConfig();
}
方案三(不推荐)
手动创建临时目录
mkdir -p /tmp/undertow.17753558642503713859.8085/undertow7370242804103803588upload
方案四(不推荐)
修改系统配置,排除该临时目录
vim /usr/lib/tmpfiles.d/tmp.con
# 文件最后添加
x /tmp/undertow*
来源:Heck's Blog
地址:https://www.heckjj.com/post/651/
转载时须以链接形式注明作者和原始出处及本声明,否则将追究法律责任,谢谢配合!