錯誤原因
在 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*