MultipartFile上传文件异步处理时的java.io.FileNotFoundException ...

鼠扑  金牌会员 | 2022-9-16 17:20:13 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 577|帖子 577|积分 1731

参考:https://javajgs.com/archives/26157
一.背景

1-1 需求

前端上传Word文档,后端将接收到的Word文档①上传到文件服务器②将Word转为Pdf。
1-2 方案

因为Word转Pdf的耗时较长,为了及时给到前端返回信息,在将文件上传到文件服务器后,异步将Word转为Pdf。

二.实现

创建一个SpringBoot项目。
  1. 1 package com.trent.upload.action;
  2. 2
  3. 3 import com.trent.upload.service.UploadService;
  4. 4 import org.springframework.web.bind.annotation.PostMapping;
  5. 5 import org.springframework.web.bind.annotation.RequestMapping;
  6. 6 import org.springframework.web.bind.annotation.RestController;
  7. 7 import org.springframework.web.multipart.MultipartFile;
  8. 8
  9. 9 import javax.annotation.Resource;
  10. 10
  11. 11 /**
  12. 12  * 上传文件的Action层
  13. 13  *
  14. 14  * @author Hutao
  15. 15  * @date 2022/8/16 15:10
  16. 16  * @since 1.0
  17. 17  */
  18. 18 @RequestMapping
  19. 19 @RestController
  20. 20 public class UploadAction {
  21. 21
  22. 22     @Resource
  23. 23     private UploadService uploadService;
  24. 24
  25. 25     /**
  26. 26      * 文件上传接口
  27. 27      * @param multipartFile 上传的文件
  28. 28      * @return 上传结果
  29. 29      *
  30. 30      * @author Hutao
  31. 31      * @date 2022/8/16 15:10
  32. 32      * @since 1.0
  33. 33      */
  34. 34     @PostMapping("/upload")
  35. 35     public String upload(MultipartFile multipartFile) {
  36. 36
  37. 37         uploadService.dealFile(multipartFile);
  38. 38         return "上传成功";
  39. 39     }
  40. 40 }
复制代码
 
  1. 1 package com.trent.upload.service;
  2. 2
  3. 3 import org.springframework.stereotype.Service;
  4. 4 import org.springframework.web.multipart.MultipartFile;
  5. 5
  6. 6 import java.util.concurrent.ExecutorService;
  7. 7 import java.util.concurrent.Executors;
  8. 8 import java.util.concurrent.TimeUnit;
  9. 9
  10. 10 /**
  11. 11  * 上传文件的Service层
  12. 12  *
  13. 13  * @author Hutao
  14. 14  * @date 2022/8/16 15:11
  15. 15  * @since 1.0
  16. 16  */
  17. 17 @Service
  18. 18 public class UploadService {
  19. 19
  20. 20     /**
  21. 21      * 线程池(仅用于简单演示)
  22. 22      */
  23. 23     private static final ExecutorService EXECUTOR_SERVICE = Executors.newCachedThreadPool();
  24. 24
  25. 25     /**
  26. 26      * 处理上传的文件
  27. 27      * @param multipartFile 上传的文件
  28. 28      *
  29. 29      * @author Hutao
  30. 30      * @date 2022/8/16 15:13
  31. 31      * @since 1.0
  32. 32      */
  33. 33     public void dealFile(MultipartFile multipartFile) {
  34. 34         // 模拟上传Word文件到文件服务器
  35. 35         System.out.println("上传Word文件到文件服务器");
  36. 36
  37. 37         // 异步将Word文件转为Pdf
  38. 38         EXECUTOR_SERVICE.execute(() -> convertToPdf(multipartFile));
  39. 39     }
  40. 40
  41. 41     /**
  42. 42      * 将文件转为Pdf
  43. 43      * @param multipartFile 待转换的源文件
  44. 44      *
  45. 45      * @author Hutao
  46. 46      * @date 2022/8/16 15:13
  47. 47      * @since 1.0
  48. 48      */
  49. 49     public void convertToPdf(MultipartFile multipartFile) {
  50. 50         try {
  51. 51            
  52. 52             // 获取上传的文件的输入流,用于转为Pdf,如果成功获取到了输入流,就认为转Pdf成功
  53. 53             multipartFile.getInputStream();
  54. 54             System.out.println("Word转Pdf成功");
  55. 55
  56. 56         } catch (Exception e) {
  57. 57             System.out.println("Word转Pdf失败");
  58. 58             e.printStackTrace();
  59. 59         }
  60. 60     }
  61. 61 }
复制代码
三.问题

3-1 问题描述

以上是一个简单的演示,在实际的项目中,会偶现如下异常。
  1. 上传Word文件到文件服务器
  2. Word转Pdf失败
  3. java.io.FileNotFoundException: C:\Users\Liujl\AppData\Local\Temp\tomcat.8080.567748920478140755\work\Tomcat\localhost\ROOT\upload_8e27d0a7_9cf4_4f8a_aecc_ea051653749e_00000006.tmp (系统找不到指定的文件。)
  4.     at java.io.FileInputStream.open0(Native Method)
  5.     at java.io.FileInputStream.open(FileInputStream.java:195)
  6.     at java.io.FileInputStream.<init>(FileInputStream.java:138)
  7.     at org.apache.tomcat.util.http.fileupload.disk.DiskFileItem.getInputStream(DiskFileItem.java:198)
  8.     at org.apache.catalina.core.ApplicationPart.getInputStream(ApplicationPart.java:100)
  9.     at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile.getInputStream(StandardMultipartHttpServletRequest.java:254)
  10.     at com.trent.upload.service.UploadService.convertToPdf(UploadService.java:57)
  11.     at com.trent.upload.service.UploadService.lambda$dealFile$0(UploadService.java:43)
  12.     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  13.     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  14.     at java.lang.Thread.run(Thread.java:748)
复制代码
意思是找不到某个临时文件。
3-2 问题复现

在UploadService类的51行添加如下代码,可保证每次上传文件操作都会出现3-1中的异常。
  1. 1     public void convertToPdf(MultipartFile multipartFile) {
  2. 2         try {
  3. 3             TimeUnit.MILLISECONDS.sleep(10);
  4. 4             // 获取上传的文件的输入流,用于转为Pdf,如果成功获取到了输入流,就认为转Pdf成功
  5. 5             multipartFile.getInputStream();
  6. 6             System.out.println("Word转Pdf成功");
  7. 7
  8. 8         } catch (Exception e) {
  9. 9             System.out.println("Word转Pdf失败");
  10. 10             e.printStackTrace();
  11. 11         }
  12. 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.本地文件使用完成后,务必删除本地文件,避免服务器硬盘被占满。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

鼠扑

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表