当前位置: 首页 > article >正文

什么!我上传的文件不见了?

什么!我上传的文件不见了?

前言:

最近在实现一个文件上传功能时使用了异步处理,但是在异步处理文件时,却提示NoSuchFileException错误。简化代码如下:

    @PostMapping("/upload")
    void testFileUpload(@RequestParam("file") MultipartFile file) {
        new Thread(() -> {
            try {
                Thread.sleep(1000);
                InputStream in = file.getInputStream();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }).start();
    }

image-20241017105746184

这个问题其实是因为MultipartFile并不持有文件,它只是映射了文件对象,文件暂时存在于Tomcat的临时目录下,在Controller层的方法执行完后,MultipartFile关联的文件就会被清除。

问题分析

要说明这个问题首先要了解MultipartFile对象的生命周期。

MultipartFile对象的生命周期

  1. 创建与初始化

    当客户端通过HTTP 请求上传文件时,Spring MVC的DispatcherServlet会拦截这个请求。根据请求中的Content-Type(通常是multipart/form-data),Spring MVC会识别出这是一个包含文件上传的请求。Spring MVC使用其内置的MultipartResolver(如CommonsMultipartResolverStandardServletMultipartResolver)来解析请求体,将文件保存到Tomcat的临时目录,并将文件数据封装成MultipartFile对象。

    MultipartFile对象在此时被创建并初始化,包含了文件的绝对路径、名称、类型、大小等信息。

    我们跟踪源码来看一下:

    1. 首先DispatcherServlet拦截到请求并执行doDispatch方法

      image-20241017135028895

      1. checkMultipart方法中,如果请求类型中包含multipart/,则会将请求内容request中的文件进行处理,把文件内容通过输出流保存到临时文件,并封装到request中的parts属性中

        image-20241017135823073

        image-20241017140200479

        image-20241017140300420

        这个方法就会将文件保存到临时目录下

        image-20241017141515572

      2. 最终,处理完成后,DispatcherServlet会将request中的文件封装为StandardMultipartFile对象,里面的part属性就来自于上面处理后的request中的part对象,值得注意的是,该Part对象只是文件的映射,并没有对文件的引用。使用文件需要通过getInputStream()方法获取输入流。

        image-20241017142358039

  2. 传递给Controller

    就是上面说的,将文件处理后,封裝为MultipartFile对象。

  3. 处理与存储

    在Controller中,开发者可以对MultipartFile对象进行进一步处理,如验证文件类型、大小、存储等。在这个过程中,MultipartFile对象作为处理文件的媒介,其生命周期与请求处理流程同步。

  4. 请求处理完成

    当Controller层的方法处理完文件上传请求后,会返回一个响应给客户端。此时,与请求相关的资源(包括MultipartFile对象)可能会被Spring MVC的底层框架(如Servlet容器)清理,以释放存储资源。也就是说在请求完成后,MultipartFile映射的临时文件可能会被清除,再通过内存中的MultipartFile去获取当前文件就会出现找不到文件的异常。

image-20241017143552110

解决办法

  1. 避免异步处理文件,对文件的操作与MultipartFile的生命周期同步。

  2. 在异步处理文件时,先将文件存储到其他地方,比如对象存储或者本地存储,之后的操作都对重新存储后新文件进行。

  3. 在进行异步前,提前获取文件的引用。比如在异步线程开始前,通过getInputStream()方法获取文件的输入流,再将输入流对象的引用传递到异步线程中。因为此时文件被引用,所以即使MultipartFile被销毁了,文件暂时也不会被清除。

      @PostMapping("/upload")
        void testFileUpload(@RequestParam("file") MultipartFile file) throws IOException {
            InputStream in = file.getInputStream();
            new Thread(() -> {
                while (true) {
                    try {
                        // todo 操作文件
                        in.read();
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }).start();
        }
    

http://www.kler.cn/a/355148.html

相关文章:

  • GOC编程 第2课 简单命令---直走和转弯命令
  • VR线上展厅的色彩管理如何影响用户情绪?
  • electron打包linux环境
  • 31.设计模式
  • UDP系统控制器_音量控制、电脑关机、文件打开、PPT演示、任务栏自动隐藏
  • 7-2 排序
  • Vue3状态管理
  • 4K分页机制相关介绍
  • 【C语言】循环结构-for循环
  • c++数据结构算法复习基础-- 5 -- 线性表-双向链表/双向循环链表-常用操作接口-复杂度分析
  • k3s安装指定版本以及离线安装(docker)
  • 多签机制简明理解及实例说明
  • GitHub每日最火火火项目(10.18)
  • 前端原型链:探索 JavaScript 中的继承奥秘
  • 宝塔下如何应对检测到存在待处理的恶意文件提醒
  • Android 通过计算器暗码启动应用
  • TCP/IP 协议【四次挥手】简要说明
  • oracle归档日志爆满问题处理
  • 遇到“mfc100u.dll丢失”的系统错误要怎么处理?科学修复mfc100u.dll
  • SAM应用:医学图像和视频中的任何内容分割中的基准测试与部署
  • 安卓-广播
  • 第J3-1周:DenseNet算法实现乳腺癌识别
  • spring-boot学习(2)
  • 从美的第二届远见者大会看AI与能源转型的未来
  • 牛客习题—线性DP 【mari和shiny】C++
  • Java后端基础自测