Linux下 Spring Boot 上傳找不到臨時目錄, 出現500錯誤

Temp Error

錯誤原因

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); 處打斷點,複製filepath 的值找到該文件並將其刪除;放開斷點,錯誤重現;

解決方案

方案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*

原文鏈接:IT浪子の博客 >Linux下 Spring Boot 上傳找不到臨時目錄, 出現500錯誤

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章