起因:之前有个需求需要对大批次的图片进行导出,同时还需要导出对应的excel表格,表格中添加对图片的超链接可以直接定位到对应图片。这样的需求有两个使用同样的接口,不同的是一个图片大小为150kb左右,另一个为1Mb左右,某次无聊测试发现1Mb的图片导出接口无论多少张都失败,且没有错误提示。
之前的代码:
由于使用了ftp连接池,以及多线程压缩,导致笔者一直认为可能是这方面的代码出现了问题,排查多次无果后,想到150kb的图片导出接口没有问题,1Mb的有问题,于是往这方面排查
/**
* 获取文件的输入流
* @param fileType
* @param fileName
* @return
*/
public InputStream getDownloadInputStream(String fileType, String fileName) {
FTPClient ftpClient = ftpPoolService.borrowObject();
ftpClient.enterLocalPassiveMode();
InputStream inputStream = null;
try {
String path = ftpPoolService.getFtpPoolConfig().getWorkingDirectory()+ DIR_SPLIT + fileType;
if(!fileType.equals(uploadImageType.BURRONLINEINSPECTION.getDescription())){
path = path + DIR_SPLIT + fileName.substring(0,8);
}
int count = 0;
while (inputStream == null){
count ++ ;
if(count == 10){
log.error("获取{}文件输入流失败",fileName);
break;
}
inputStream = ftpClient.retrieveFileStream(path + DIR_SPLIT + fileName);
log.info("当前文件{} 输入流是否为空:{}",fileName,inputStream == null?"是":"否");
}
if(inputStream == null){
return new NullInputStream(0);
}
return inputStream;
} catch (Exception e) {
log.error("获取文件输入流失败",e);
}finally {
ftpPoolService.returnObject(ftpClient);
}
return new NullInputStream(0);
}
最终定位到是由于多线程环境下,ftpClient.retrieveFileStream并不能一次获取到完整的输入流,直接返回归还ftp连接失败,最终一直阻塞,直到超时。所以需要在获取完后关闭输入流,并且通过
ftpClient.completePendingCommand();
来表明输入流已获取完,修改后的代码如下:
public byte[] getFileInputStream(String fileType, String fileName) throws IOException {
FTPClient ftpClient = ftpPoolService.borrowObject();
ftpClient.enterLocalPassiveMode();
InputStream inputStream = null;
byte[] bytes = null;
try {
String path = ftpPoolService.getFtpPoolConfig().getWorkingDirectory()+ DIR_SPLIT + fileType + DIR_SPLIT + fileName.substring(0,8);
int count = 0;
while (inputStream == null){
count ++ ;
if(count == 10){
log.error("获取{}文件输入流失败",fileName);
break;
}
inputStream = ftpClient.retrieveFileStream(path + DIR_SPLIT + fileName);
log.info("当前文件{} 输入流是否为空:{}",fileName,inputStream == null?"是":"否");
}
if(inputStream == null){
return new byte[0];
} else{
bytes = IOUtils.toByteArray(inputStream);
if (inputStream != null) {
inputStream.close();
}
}
return bytes;
} catch (Exception e) {
log.error("获取文件输入流失败",e);
}finally {
ftpClient.completePendingCommand();
ftpPoolService.returnObject(ftpClient);
}
return new byte[0];
}
外部使用
InputStream inputStream = new ByteArrayInputStream(fileInputStream);
将字节数组转换为输入流即可。