1. 程式人生 > 其它 >Spring Boot 應用上傳檔案時報錯

Spring Boot 應用上傳檔案時報錯

技術標籤:Spring Boot

Spring Boot

目錄

問題描述

Spring Boot 應用(使用預設的嵌入式 Tomcat)在上傳檔案時,偶爾會出現上傳失敗的情況,後臺報錯日誌資訊如下:“The temporary upload location is not valid”。

原因追蹤

這個問題的根本原因是 Tomcat 的檔案上傳機制引起的!
Tomcat 在處理檔案上傳時,會將客戶端上傳的檔案寫入臨時目錄,這個臨時目錄預設在 /tmp 路徑下,如:“/tmp/tomcat.6574404581312272268.18333/work/Tomcat/localhost/ROOT”。

而作業系統對於 /tmp 目錄會不定時進行清理,如果正好因為作業系統的清理導致對應的臨時目錄被刪除,客戶端再上傳檔案時就會報錯:“The temporary upload location is not valid”。
實際上,追蹤一下原始碼會發現,如果不明確設定 Tomcat 的檔案上傳臨時目錄,預設讀取的是 Servlet 上下文物件的屬性 “javax.servlet.context.tempdir” 值,如下原始碼:

  • org.apache.catalina.connector.Request
private void parseParts(boolean explicit) {
    //...
MultipartConfigElement mce = this.getWrapper().getMultipartConfigElement(); //... // 讀取MultipartConfigElement物件的location屬性 String locationStr = mce.getLocation(); File location; if (locationStr != null && locationStr.length() != 0) { location = new File(locationStr)
; if (!location.isAbsolute()) { location = (new File((File)context.getServletContext().getAttribute("javax.servlet.context.tempdir"), locationStr)).getAbsoluteFile(); } } else { // 如果location屬性值為空,則讀取Servlet上下文物件的屬性“javax.servlet.context.tempdir”值(如:/tmp/tomcat.6574404581312272268.18333/work/Tomcat/localhost/ROOT) location = (File)context.getServletContext().getAttribute("javax.servlet.context.tempdir"); } //... }

解決辦法

既然是因為上傳檔案的臨時路徑被刪除導致的問題,就要確保改臨時目錄不會被刪除。
2 種解決方法:
(1) 通過 Spring Boot 的配置引數 “spring.servlet.multipart.location” 明確指定上傳檔案的臨時目錄,確保該路徑已經存在,而且該目錄不會被作業系統清除。

spring.servlet.multipart.location=/data/tmp

如上所示,將上傳檔案的臨時目錄指定到路徑 “/data/tmp” 下。

實際上,在 Spring Boot 中關於上傳檔案的所有配置引數如下所示:

# MULTIPART (MultipartProperties)
spring.servlet.multipart.enabled=true # Whether to enable support of multipart uploads.
spring.servlet.multipart.file-size-threshold=0B # Threshold after which files are written to disk.
spring.servlet.multipart.location= # Intermediate location of uploaded files.
spring.servlet.multipart.max-file-size=1MB # Max file size.
spring.servlet.multipart.max-request-size=10MB # Max request size.
spring.servlet.multipart.resolve-lazily=false # Whether to resolve the multipart request lazily at the time of file or parameter access.

(2) 在 Spring 容器中明確註冊 MultipartConfigElement 物件,通過 MultipartConfigFactory 指定一個路徑。
在上述原始碼追蹤中就發現,Tomcat 會使用 MultipartConfigElement 物件的 location 屬性作為上傳檔案的臨時目錄。

/**
 * 配置上傳檔案臨時目錄
 * @return
 */
@Bean
public MultipartConfigElement multipartConfigElement() {
    MultipartConfigFactory factory = new MultipartConfigFactory();
    // tmp.dir引數在啟動指令碼中設定
    String path = System.getProperty("tmp.dir");
    if(path == null || "".equals(path.trim())) {
        path = System.getProperty("user.dir");
    }
    String location = path + "/tmp";
    File tmpFile = new File(location);
    // 如果臨時目錄不存在則建立
    if (!tmpFile.exists()) {
        tmpFile.mkdirs();
    }
    // 明確指定上傳檔案的臨時目錄
    factory.setLocation(location);
    return factory.createMultipartConfig();
}

【參考】
https://stackoverflow.com/questions/50523407/the-temporary-upload-location-tmp-tomcat-4296537502689403143-5000-work-tomcat/50523578 The temporary upload location is not valid
https://blog.csdn.net/llibin1024530411/article/details/79474953 SpringBoot 專案的 The temporary upload location ***is not valid 問題