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

本文探讨了SpringBoot中使用异步线程进行文件上传时出现的FileNotFoundException问题,并提供了两种解决方案,一种是在主线程中获取输入流,另一种是直接使用输入流进行文件写入。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

一、问题描述

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

        报错信息如下:

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 (系统找不到指定的文件。)
	at org.apache.catalina.core.ApplicationPart.write(ApplicationPart.java:122)

二、模拟实现

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

/**
 * @Description: 演示 异步上传 - file not found 问题 --- simple 版
 * @param file
 * @return  void
 * @version v1.0
 * @author wu
 * @date 2022/9/25 20:09
 */
@RequestMapping("/up")
public String up(MultipartFile file){
    ExecutorService executor = Executors.newSingleThreadExecutor();
    executor.submit(()->{
        try {
            Thread.sleep(1000L);
            file.transferTo(new File("D:/",new Date().getTime()+file.getOriginalFilename()));
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+" ==> 异步上传完成!");
    });

    return file.getOriginalFilename()+":文件上传成功!";
}

        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 (系统找不到指定的文件。)
	at org.apache.catalina.core.ApplicationPart.write(ApplicationPart.java:122)
	at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile.transferTo(StandardMultipartHttpServletRequest.java:255)
	at com.runcode.springboottourist.upload.web.UploadAsyncController.lambda$up$2(UploadAsyncController.java:187)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)

pool-4-thread-1 ==> 异步上传完成!

三、问题解决

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

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

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

    return file.getOriginalFilename()+":文件上传成功!";
}

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

@RequestMapping("/up/fix2")
    public String upFix2(MultipartFile file) throws IOException {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        /**
         * 使用 inputStream 作为入参,解决 file not found 问题
         */
        InputStream inputStream = file.getInputStream();
        String filename = file.getOriginalFilename();
        executor.submit(()->{
            try {
                Thread.sleep(1000L);

                // 使用 spring的 FileCopyUtils
                FileCopyUtils.copy(inputStream, Files.newOutputStream(new File("D:/", new Date().getTime() + filename).toPath()));
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }finally {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+" ==> 异步上传完成!");
        });

        return file.getOriginalFilename()+":文件上传成功!";
    }

四、总结

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

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

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

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

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

https://blog.youkuaiyun.com/HaHa_Sir/article/details/127044489



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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值