1.前言
在生产开发中大多数的文件上传备份需求我们希望它是和主业务线程处于异步情况下的,这样可以更快的响应给用户反馈,减少等待时间。基于这样的想法,在最近的开发中也是打算如此完成业务实现,却是遇到了
MultipartFile异步保存文件transferto 报错java.io.FileNotFoundException: XXX.tmp (No such file or directory)
这样的报错。
2.解析
经过测试发现在主线程没有完全结束的情况下MultipartFile对象都还是好好的,但是在主线程完成响应后,打印的MultipartFile对象就变成了null,于是乎后面就报错了。
由于SpringBoot上传文件后会形成MultiPartFile的实例,在临时文件夹中生成临时文件,并且此实例执行此临时文件。当主线程执行结束后,
SpringMVC会清除掉此临时文件,导致子线程在处理此实例的时候找不到此实例指向的临时文件,从而报FileNotFoundException!
传MultipartFile对象不行我就换成传InputStrem 的方式了
3.实现
首先需要提醒的是要使用@Async的话需要注意
启动类中先增加@EnableAsync
使用ThreadPoolTaskExecutor 配置自定义线程池
@Async的原理概括:
@Async 的原理是通过 Spring AOP 动态代理 的方式来实现的。
Spring容器启动初始化bean时,判断类中是否使用了@Async注解,如果使用了则为其创建切入点和切入点处理器,根据切入点创建代理,
在线程调用@Async注解标注的方法时,会调用代理,执行切入点处理器invoke方法,将方法的执行提交给线程池中的另外一个线程来处理,从而实现了异步执行。
所以,需要注意的一个错误用法是,如果a方法调用它同类中的标注@Async的b方法,是不会异步执行的,因为从a方法进入调用的都是该类对象本身,不会进入代理类。
相同类中的方法调用带@Async的方法是无法异步的,这种情况仍然是同步。
@Async("task")
private void saveFile(InputStrem file,String filePath ){
/*
创建路径目录
。。。。。。。。。。。。
*/
try (BufferedInputStream in=new BufferedInputStream (file);
BufferedOutputStream out=new BufferedOutputStream (new FileOutputStream(filePath )) ){
int len;
byte[] bytes = new byte[1024];
while ((len = in.read(bytes)) != -1) {
out.write(bytes, 0, len);
}
out.flush();
} catch (IOException ex) {
.........
}