參考:https://javajgs.com/archives/26157
一.背景
1-1 需求
前端上傳Word檔案,后端將接收到的Word檔案①上傳到檔案服務器②將Word轉為Pdf,
1-2 方案
因為Word轉Pdf的耗時較長,為了及時給到前端回傳資訊,在將檔案上傳到檔案服務器后,異步將Word轉為Pdf,

二.實作
創建一個SpringBoot專案,
1 package com.trent.upload.action; 2 3 import com.trent.upload.service.UploadService; 4 import org.springframework.web.bind.annotation.PostMapping; 5 import org.springframework.web.bind.annotation.RequestMapping; 6 import org.springframework.web.bind.annotation.RestController; 7 import org.springframework.web.multipart.MultipartFile; 8 9 import javax.annotation.Resource; 10 11 /** 12 * 上傳檔案的Action層 13 * 14 * @author Hutao 15 * @date 2022/8/16 15:10 16 * @since 1.0 17 */ 18 @RequestMapping 19 @RestController 20 public class UploadAction { 21 22 @Resource 23 private UploadService uploadService; 24 25 /** 26 * 檔案上傳介面 27 * @param multipartFile 上傳的檔案 28 * @return 上傳結果 29 * 30 * @author Hutao 31 * @date 2022/8/16 15:10 32 * @since 1.0 33 */ 34 @PostMapping("/upload") 35 public String upload(MultipartFile multipartFile) { 36 37 uploadService.dealFile(multipartFile); 38 return "上傳成功"; 39 } 40 }
1 package com.trent.upload.service; 2 3 import org.springframework.stereotype.Service; 4 import org.springframework.web.multipart.MultipartFile; 5 6 import java.util.concurrent.ExecutorService; 7 import java.util.concurrent.Executors; 8 import java.util.concurrent.TimeUnit; 9 10 /** 11 * 上傳檔案的Service層 12 * 13 * @author Hutao 14 * @date 2022/8/16 15:11 15 * @since 1.0 16 */ 17 @Service 18 public class UploadService { 19 20 /** 21 * 執行緒池(僅用于簡單演示) 22 */ 23 private static final ExecutorService EXECUTOR_SERVICE = Executors.newCachedThreadPool(); 24 25 /** 26 * 處理上傳的檔案 27 * @param multipartFile 上傳的檔案 28 * 29 * @author Hutao 30 * @date 2022/8/16 15:13 31 * @since 1.0 32 */ 33 public void dealFile(MultipartFile multipartFile) { 34 // 模擬上傳Word檔案到檔案服務器 35 System.out.println("上傳Word檔案到檔案服務器"); 36 37 // 異步將Word檔案轉為Pdf 38 EXECUTOR_SERVICE.execute(() -> convertToPdf(multipartFile)); 39 } 40 41 /** 42 * 將檔案轉為Pdf 43 * @param multipartFile 待轉換的源檔案 44 * 45 * @author Hutao 46 * @date 2022/8/16 15:13 47 * @since 1.0 48 */ 49 public void convertToPdf(MultipartFile multipartFile) { 50 try { 51 52 // 獲取上傳的檔案的輸入流,用于轉為Pdf,如果成功獲取到了輸入流,就認為轉Pdf成功 53 multipartFile.getInputStream(); 54 System.out.println("Word轉Pdf成功"); 55 56 } catch (Exception e) { 57 System.out.println("Word轉Pdf失敗"); 58 e.printStackTrace(); 59 } 60 } 61 }
三.問題
3-1 問題描述
以上是一個簡單的演示,在實際的專案中,會偶現如下例外,
上傳Word檔案到檔案服務器 Word轉Pdf失敗 java.io.FileNotFoundException: C:\Users\Liujl\AppData\Local\Temp\tomcat.8080.567748920478140755\work\Tomcat\localhost\ROOT\upload_8e27d0a7_9cf4_4f8a_aecc_ea051653749e_00000006.tmp (系統找不到指定的檔案,) at java.io.FileInputStream.open0(Native Method) at java.io.FileInputStream.open(FileInputStream.java:195) at java.io.FileInputStream.<init>(FileInputStream.java:138) at org.apache.tomcat.util.http.fileupload.disk.DiskFileItem.getInputStream(DiskFileItem.java:198) at org.apache.catalina.core.ApplicationPart.getInputStream(ApplicationPart.java:100) at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile.getInputStream(StandardMultipartHttpServletRequest.java:254) at com.trent.upload.service.UploadService.convertToPdf(UploadService.java:57) at com.trent.upload.service.UploadService.lambda$dealFile$0(UploadService.java:43) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)
意思是找不到某個臨時檔案,
3-2 問題復現
在UploadService類的51行添加如下代碼,可保證每次上傳檔案操作都會出現3-1中的例外,
1 public void convertToPdf(MultipartFile multipartFile) { 2 try { 3 TimeUnit.MILLISECONDS.sleep(10); 4 // 獲取上傳的檔案的輸入流,用于轉為Pdf,如果成功獲取到了輸入流,就認為轉Pdf成功 5 multipartFile.getInputStream(); 6 System.out.println("Word轉Pdf成功"); 7 8 } catch (Exception e) { 9 System.out.println("Word轉Pdf失敗"); 10 e.printStackTrace(); 11 } 12 }
3-3 原因分析
1.后臺用MultipartFile接收到前端傳來的檔案后,會在本地生成一個臨時檔案,以.tmp結尾;
2.MultipartFile對應的臨時檔案的生命周期是一個請求會話,會話結束,MultipartFile的臨時檔案會被自動清理;
3.因為將檔案轉為Pdf的方法是在請求主執行緒之外的另一個執行緒中執行的,所以不在請求會話的生命周期內,如果請求會話的主執行緒結束了(將請求結果回傳給前端了),這個請求傳來的MultipartFile的臨時檔案就會被清理掉,在將檔案轉Pdf的執行緒中就拿不到MultipartFile對應的臨時檔案,也就獲取不到對應的輸入流,故拋出FileNotFoundException,
四.解決方案
主執行緒在用MultipartFile接收到前端傳來檔案后,立即將MultipartFile保存為本地檔案,將檔案轉Pdf時,使用保存在本地的檔案,轉換完成后,洗掉本地檔案,
注:
1.可使用MultipartFile的transferTo方法將MultipargFile轉為本地檔案,但需要注意,transferTo方法被呼叫后,也會洗掉MultipartFile對應的臨時檔案;
2.在使用transferTo的時候可能會出現絕對路徑和相對路徑的問題;
3.鑒于1.2中transferTo方法的局限性,建議手動獲取MultipartFile的輸入流,然后寫到本地檔案中,可以使用Hutool的FileUtils.copyInputStreamToFile(final InputStream source, final File destination)方法;
4.本地檔案使用完成后,務必洗掉本地檔案,避免服務器硬碟被占滿,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/501984.html
標籤:其他
下一篇:在使用amoeba連接資料庫時,報錯java.lang.Exception: poolName=slaves, no valid pools
