问题: 当接口中传递文件流给后端处理时, 可能会面临两个问题 :
1, 文件流处理一般时间较长, 为了避免接口长时间等待,我们一般在异步线程中处理文件流, 但是当同步线程已经返回, 我们在异步线程中使用该流对象就会遇见一个问题: IOException: stream closed(文件流已关闭)
2, 文件流对象在读取之后, 读取指针会不断移动, 对于不支持mark and reset操作的流对象,我们就会遇到一个文件流无法二次读取的问题
1, 将流通道中的数据保存到本地文件中, 解决异步线程流关闭的问题
将输入流中的数据保存到本地文件中的方法有多种, 这里介绍两种常用的方式:
- 使用 jdk自带的 Files.copy()方法
Files.copy(inputStream, Paths.get(targetPath), StandardCopyOption.REPLACE_EXISTING);
- 使用commons-io包下的FileUtils工具类(可以自动关流)
FileUtils.copyFile(inputSteam ,new File(targetPath));
2, 对文件流对象进行拷贝,解决文件流不可重用的问题
- 哪些流对象执行mark and reset (注意: 要想重复读取流中的数据就只能在最后一次读取流数据结束之后才关流, 提前关流会造成后面的流读取报错)
对于处于次级的流对象 FileInputstream, FilterInpustream都没有重写父级 Inpustream接口的mark and reset 方法, 如果调用是直接抛出异常.
如果要使用这个两个方法, 必须是 BufferedInputStream 或者 ByteArrayInputStream, 因为这两个流对象重写Inpustream接口中的mark and reset方法
使用场景举例:
对于前端上传的文件, 后端为了防止文件攻击, 需要校验上传的文件类型, 为了获取真实的文件类型, 我们需要读取文件流中前28个byte, 但是校验不能影响后续的业务逻辑, 这样就需要对文件流进行重复读取, MultipartFile对象获取的流对象是支持mark and reset操作
// 校验文件类型;
String fileType;
BufferedInputStream bufferedInputStream; // 不要关闭这个流, 后续还会继续读取
try {
bufferedInputStream = new BufferedInputStream(file.getInputStream());
bufferedInputStream.mark(28);
fileType = FileTypeUtil.getType(bufferedInputStream, originalFilename);
bufferedInputStream.reset();
} catch (IOException e) {
log.error("获取文件流异常:", e);
throw new AppException("获取文件流异常");
}
- 对于无法调用 mark and reset方法的流对象, 如果我们想要重复读取该流通道中的数据, 就需要对流对象进行拷贝
InputStream inputStream = new FileInputStream(targetPath);
// boolean flag = inputStream.markSupported();
// System.out.println(flag);
// 创建字节数组输出流对象
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// 创建 4kb的缓存数组
byte[] bytes = new byte[4*1024];
int length;
while((length = inputStream.read(bytes)) != -1) {
byteArrayOutputStream.write(bytes,0, length);
byteArrayOutputStream.flush();
}
// 拷贝新的输入流
InputStream copyInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());