最近做了一个异步上传解析excel功能,最后联调时偶发出现了FileNotFoundException异常:
1、现象
java.io.FileNotFoundException: /tmp/tomcat.4469822161725136903.8080/work/Tomcat/localhost/ROOT/upload_4bed117f_51fb_4d70_b307_fc00edd99e4a_00000029.tmp (No such file or directory)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at org.apache.tomcat.util.http.fileupload.disk.DiskFileItem.getInputStream(DiskFileItem.java:194)
at org.apache.catalina.core.ApplicationPart.getInputStream(ApplicationPart.java:100)
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile.getBytes(StandardMultipartHttpServletRequest.java:245)
大致代码如下:
@PostMapping(value = "/asyncImport")
public Boolean asyncImport(@RequestParam("file") MultipartFile file) {
try {
//异步接口
importService.asyncImport(file.getInputStream());
return true;
} catch (IOException e) {
throw new RuntimeException("出现错误");
}
}
@Async
public String asyncImport(InputStream inputStream) {
//执行操作
return "ok";
}
2.原因:
这个时候会有概率抛出java.io.FileNotFoundException:
因为 Service方法为异步方法, controller会立马执行完成, 完成后便会关闭IO流, 最后会导致文件找不到
3.解决方案
1.得到MultipartFile的byte数组,将byte数组传入service,租后转换成IO流
byte[] bytes = MultipartFile.getBytes();
优点:比较快,比较方便
缺点:大文件是可能为导致频繁fullgc,甚至是OOM
2.将MultipartFile写入到一个临时文件里,等用完再删除
优点:支持大文件
缺点:效率比较低,需要写入临时文件
方法1:直接byte数组类型
@PostMapping(value = "/asyncImport")
public Boolean asyncImportForByteArray(@RequestParam("file") MultipartFile file) {
try {
// 获取字节数组:
byte[] bytes = file.getBytes();
//异步接口
importService.asyncImportForByteArray(bytes);
return true;
} catch (IOException e) {
throw new RuntimeException("出现错误");
}
}
@Async
public String asyncImportForByteArray(byte[] bytes) {
try {
//**** 1.获取流
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
// 2.执行操作
return "ok";
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
} finally {
}
}
方法2:临时文件方式
/**
* 上传的路径前缀
*/
public static final String UPLOAD_PREFIX_PATH = "/opt/uploading";
@PostMapping(value = "/asyncImport")
public Boolean asyncImportForTempFile(@RequestParam("file") MultipartFile file) {
try {
// 生成临时文件
File tempDirectory = new File(UPLOAD_PREFIX_PATH);
if (!tempDirectory.exists()) {
FileUtils.forceMkdir(tempDirectory);
}
File tempFile = new File(tempDirectory, file.getOriginalFilename());
file.transferTo(tempFile);
//异步接口
importService.asyncImportForTempFile(tempFile);
return true;
} catch (IOException e) {
throw new RuntimeException("出现错误");
}
}
@Async
public String asyncImportForTempFile(File tempFile) {
try {
// 1.获取流
FileInputStream inputStream = FileUtils.openInputStream(tempFile);
// 2.执行操作
return "ok";
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
} finally {
// chuan
FileUtils.deleteQuietly(tempFile.getParentFile());
}
}
···
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
····