自学内容网 自学内容网

SpringBoot:SpringBoot异步上传文件报错

一、前言

  项目中有个接口的功能是这样的,客户端先上传文件之后,服务端需要对文件进行一些校验,以及记录文件信息之后再把文件上传到公有云服务器,最后把一些传递给其他系统。因为文件比较大,使用同步调用的时候,客户端在等待的时间有点久,所以想着把接口改成异步调用,让服务端异步操作完了之后再发送成功的消息

  实现方式其实很简单,就是把需要的操作方法改为异步方法,使用@Async注解。

但是运行之后确出现了

java.io.FileNotFoundException: /data/tomcat/yytx/work/Catalina/localhost/ROOT/upload_8ed3adae_e26f_49be_a005_9b0ad7f6be31_00000005.tmp (No such file or directory)

可以看到是这个临时文件不存在

二、问题排查

  方法一:我的文件是放在了System.getProperty(“java.io.tmpdir”),tomcat的这个临时目录下面,然后在上传,起初怀疑是临时目录会被删除导致的,所以我自定义了一个目录,用来存放文件,在文件操作完成之后再删除。但是改完之后,还是有问题。

   方法二:排查方法,增加一些打印日志,upload_8ed3adae_e26f_49be_a005_9b0ad7f6be31_00000005.tmp (No such file or directory)看报错是文件没找到,那么重点观察MultipartFile文件上传之后的每一步情况

结论:Controller接口异步调用之后,再接口返回的时候,就会关闭MultipartFile文件

三、问题分析

MultipartFile生命周期了解

1. 创建与初始化

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

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

2. 传递给Controller

  在Spring MVC的请求处理流程中,MultipartFile对象会被作为参数传递给Controller层中处理文件上传的方法。这个过程是通过Spring的MVC参数绑定机制实现的,即Spring会根据请求中的数据和方法的参数类型进行匹配,并将请求中的数据绑定到方法的参数上。

3. 处理与存储

  在Controller层的方法中,开发者可以对MultipartFile对象进行进一步的处理,如验证文件类型、大小等
如果需要,开发者可以将文件保存到服务器的磁盘上,或者将文件内容转换为其他格式进行存储(如保存到数据库)。
在这个过程中,MultipartFile对象作为处理文件上传的媒介,其生命周期与请求处理流程同步。

4. 请求处理完成

  当Controller层的方法处理完文件上传请求后,会返回一个响应给客户端。此时,与请求相关的资源(包括MultipartFile对象)可能会被Spring MVC的底层框架(如Servlet容器)清理,以释放内存或资源。

  但是,需要注意的是,MultipartFile对象本身并不直接管理文件在服务器上的存储。如果开发者已经将文件保存到了磁盘或数据库中,那么这些文件将独立于MultipartFile对象存在。

5. 生命周期结束
  MultipartFile对象的生命周期在Controller层的方法执行完毕后结束。但是,由于MultipartFile对象并不直接管理文件存储,因此文件的实际生命周期取决于开发者如何处理和存储这些文件。

  如果开发者在异步任务中继续使用MultipartFile对象(尽管这通常不是推荐的做法),则需要确保在异步任务执行完毕前,MultipartFile对象所引用的文件数据仍然可用。

  这通常意味着需要在异步任务开始之前将文件数据保存到某个持久化存储中,并在异步任务中使用这个存储路径来访问文件。

四、解决方法

  在异步方法之前,将MultipartFile对象落盘或者把Inputstream提前获取。


使用transferTo

// 文件先保存到本地
Path filePath = Paths.get(filePath, fileName);
// 如果目标文件夹不存在,则创建它
if (!Files.exists(filePath.getParent())) {
Files.createDirectories(localFilePath.getParent());
}

file.transferTo(filePath.toFile());


原文地址:https://blog.csdn.net/A79800/article/details/140370596

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!