• Spring Boot 文件上传 报错:FileNotFoundException Spring 异步文件上传 FileNotFoundException


     Spring Boot 文件上传 报错:FileNotFoundException Spring Boot异步文件上传 FileNotFoundException

    一、问题描述

            在使用Spring Boot做文件上传的过程中,遇到上传文件报错 FileNotFoundException 问题,查了一圈,都是说要配置上传文件路径问题,经过仔细的分析和测试,发现不是配置路径的问题 (在主线程中,没配置路径,可以正常实现上传!),而是用了异步上传的问题导致的。

            报错信息如下:

    1. java.io.IOException: java.io.FileNotFoundException: C:\Users\xxx\AppData\Local\Temp\tomcat.7137654154096270302.8080\work\Tomcat\localhost\ROOT\upload_f6b6398b_fe9b_41af_9579_4a835264fef3_00000000.tmp (系统找不到指定的文件。)
    2. at org.apache.catalina.core.ApplicationPart.write(ApplicationPart.java:122)

    二、模拟实现

            1、使用 异步线程上传文件,抛出 FileNotFoundException 异常

    1. /**
    2. * @Description: 演示 异步上传 - file not found 问题 --- simple 版
    3. * @param file
    4. * @return void
    5. * @version v1.0
    6. * @author wu
    7. * @date 2022/9/25 20:09
    8. */
    9. @RequestMapping("/up")
    10. public String up(MultipartFile file){
    11. ExecutorService executor = Executors.newSingleThreadExecutor();
    12. executor.submit(()->{
    13. try {
    14. Thread.sleep(1000L);
    15. file.transferTo(new File("D:/",new Date().getTime()+file.getOriginalFilename()));
    16. } catch (IOException | InterruptedException e) {
    17. e.printStackTrace();
    18. }
    19. System.out.println(Thread.currentThread().getName()+" ==> 异步上传完成!");
    20. });
    21. return file.getOriginalFilename()+":文件上传成功!";
    22. }

            1.1、运行结果:

    1. java.io.IOException: java.io.FileNotFoundException: C:\Users\xxx\AppData\Local\Temp\tomcat.7137654154096270302.8080\work\Tomcat\localhost\ROOT\upload_f6b6398b_fe9b_41af_9579_4a835264fef3_00000000.tmp (系统找不到指定的文件。)
    2. at org.apache.catalina.core.ApplicationPart.write(ApplicationPart.java:122)
    3. at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile.transferTo(StandardMultipartHttpServletRequest.java:255)
    4. at com.runcode.springboottourist.upload.web.UploadAsyncController.lambda$up$2(UploadAsyncController.java:187)
    5. at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    6. at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    7. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    8. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    9. at java.lang.Thread.run(Thread.java:745)
    10. pool-4-thread-1 ==> 异步上传完成!

    三、问题解决

            1、原来 MultipartFile 对象在文件上传的时候,会生成 临时文件,此时生成的临时文件在主线程中;而异步线程在操作 MultipartFile 对象时,此时主线程中的临时文件,将会被 spring 给删除了,也就造成异常 FileNotFound !

            2、办法一:在主线中获取到输入流对象,可以解决 文件被删除的问题: InputStream inputStream = file.getInputStream();

    1. /**
    2. * @Description: 解决异步线程中 file not found 问题
    3. *
      主线程中: file.getInputStream(); , 应该是会生成临时文件的
    4. * @param file
    5. * @return java.lang.String
    6. * @version v1.0
    7. * @author wu
    8. * @date 2022/9/25 20:43
    9. */
    10. @RequestMapping("/up/fix")
    11. public String upFix(MultipartFile file) throws IOException {
    12. ExecutorService executor = Executors.newSingleThreadExecutor();
    13. /**
    14. * 1. 可以解决 异步线程中 file not found 的问题
    15. * file.getInputStream(); 这行代码不能删掉。
    16. */
    17. InputStream inputStream = file.getInputStream();
    18. executor.submit(()->{
    19. try {
    20. Thread.sleep(1000L);
    21. file.transferTo(new File("D:/",new Date().getTime()+file.getOriginalFilename()));
    22. } catch (IOException | InterruptedException e) {
    23. e.printStackTrace();
    24. }
    25. System.out.println(Thread.currentThread().getName()+" ==> 异步上传完成!");
    26. });
    27. return file.getOriginalFilename()+":文件上传成功!";
    28. }

            3、办法二:使用 inputStream流作为入参

    1. @RequestMapping("/up/fix2")
    2. public String upFix2(MultipartFile file) throws IOException {
    3. ExecutorService executor = Executors.newSingleThreadExecutor();
    4. /**
    5. * 使用 inputStream 作为入参,解决 file not found 问题
    6. */
    7. InputStream inputStream = file.getInputStream();
    8. String filename = file.getOriginalFilename();
    9. executor.submit(()->{
    10. try {
    11. Thread.sleep(1000L);
    12. // 使用 spring的 FileCopyUtils
    13. FileCopyUtils.copy(inputStream, Files.newOutputStream(new File("D:/", new Date().getTime() + filename).toPath()));
    14. } catch (IOException | InterruptedException e) {
    15. e.printStackTrace();
    16. }finally {
    17. try {
    18. inputStream.close();
    19. } catch (IOException e) {
    20. e.printStackTrace();
    21. }
    22. }
    23. System.out.println(Thread.currentThread().getName()+" ==> 异步上传完成!");
    24. });
    25. return file.getOriginalFilename()+":文件上传成功!";
    26. }

    四、总结

            1、本文以极简的示例,模拟出现异常的情况,完整的 Spring Boot 文件上传下载,可以参考:Spring Boot 实现文件上传和下载 以及上传后访问文件_HaHa_Sir的博客-CSDN博客_springboot上传文件访问

            2、异步线程操作,总会遇到一些莫名其妙的问题,需要静下心来,认真思考,多尝试,多反思: 在 主线程中操作,会出现这个情况吗

            3、办法一 和 办法二 很类似,其中 办法一,获取到 inputStream对象后,也没有进行后续的操作,不知道为啥是可以的 ...

            4、实际开发中,需要使用 异步文件上传时候,建议使用 【办法二】,以参数的形式传递变量到异步线程中 。 因为【办法一】中 inputStream 变量,没有被使用,会被一些 IDE 提示,无效的变量,建议删除; 若真的删除了 ... 就会有 奇怪的错误出现~~

    关于异步线程可能出现的问题,了解更多:

    https://blog.csdn.net/HaHa_Sir/article/details/127044489



    Spring Boot 实现文件上传和下载 以及上传后访问文件_HaHa_Sir的博客-CSDN博客_springboot上传文件访问

  • 相关阅读:
    MySQL——DQL union合并、limit限制与DDL建表和删表
    cookie、session、token的关系及测试
    教程|fNIRS数据处理工具包Homer2下载与安装
    卷积神经网络-卷积层
    骑士周游 ---递归的说明
    【Linux基础】中断子系统 -- 内核宏 CONFIG_SPARSE_IRQ
    初阶JavaEE(17)Linux 基本使用和 web 程序部署
    IT项目中需要规避的9类风险,你知道都有哪些吗?
    【Coppeliasim】 通过TCP与coppeliasim通信
    Nmap使用教程图文教程(超详细)
  • 原文地址:https://blog.csdn.net/HaHa_Sir/article/details/127044832