關於Spring應用異步上傳文件MultipartFile時報FileNotFoundException

背景

因爲本身項目(springboot後端服務)和文檔編輯相關,因此會存在大量的文件上傳oss操做,過程當中存在有大文件的上傳,爲了避免影響體驗,後端服務拿到文件流後直接返回成功,而後交給子線程異步調用oss上傳服務。java

問題

起初測試什麼的根本沒發現這個問題,感受也不是必現的。後來在排查其餘問題的時候查看系統日誌的時候,偶爾會發現一段java.io.IOException:java.io.FileNotFoundException:/home/admin/appName/.default/temp/tomcat.4504264197870423949.7001/work/Tomcat/Localhost/ROOT/upLoad_ff92855a_13c6_49d9_bbdf_1c062fb9bfd9_00000004.tmp(沒有那個文件或目錄)的錯誤。web

定位問題

在後端controller入參中是以MultipartFile來包裝文件流的。在註釋上看的很清楚,會在請求結束後清理掉臨時文件。因爲咱們是調用異步線程來處理最終的文件上傳,因此當主線程返回時,清理掉文件時,就會報沒有那個文件或目錄了。

在springboot的自動配置模塊中,有MultipartAutoConfiguration類會默認加載StandardServletMultipartResolver類做爲後續bean的元信息。
在初始化web子容器時,會初始化StandardServletMultipartResolver bean做爲文件解析器。

在org.springframework.web.servlet.DispatcherServlet#doDispatch方法中處理請求時,最終checkMultipart的調用會調用上述加載的解析器進行處理org.springframework.web.multipart.support.StandardServletMultipartResolver#resolveMultipart

該實現是將request包裝成StandardMultipartHttpServletRequest,因爲是非懶解析

setMultipartFiles方法是AbstractMultipartHttpServletRequest中的方法,該方法會設值multipartFiles。

在doDispatch方法處理完主線程的請求後,就會清理文件,會委託給org.springframework.web.multipart.support.StandardServletMultipartResolver#cleanupMultipart來處理

如圖,StandardMultipartHttpServletRequest是AbstractMultipartHttpServletRequest的子類,isResolved的判斷就是AbstractMultipartHttpServletRequest中的屬性multipartFiles是否有值,上述分析是有值的,因此就觸發了part.delete。

這樣的話,在異步線程再去取文件時就報錯了!spring

處理

使用org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.StandardMultipartFile#getBytes方法,該方法會調用org.springframework.util.FileCopyUtils#copyToByteArray生產一個新的字節數組保存數據,以免原始數據刪除以後帶來的子線程獲取文件失敗。
相關文章
相關標籤/搜索