目录
写在前面
测试
测试
总结
写在前面
有很多互联网web项目,做文件打包下载的时候,可能需要先从文件服务器下载所有文件放到临时目录,然后再将目录打包成zip临时文件,然后再返回给前端。
其实这种方式是可以实现业务逻辑的,但是事实上效率真的高吗?
也许可以优化!
公共文件下载方法
/**
* 文件下载
*/
public void downloadFile(HttpServletResponse response, BufferedInputStream bis, String fileName) {
OutputStream os = null;
BufferedOutputStream bos = null;
try {
//文件流的声明
//重点突出(特别注意),通过response获取的输出流,作为服务端向客户端浏览器输出内容的通道
os = response.getOutputStream();
// 为了提高效率使用缓冲区流
bos = new BufferedOutputStream(os);
// 对文件名进行编码处理中文问题
// 处理中文文件名的问题
fileName = java.net.URLEncoder.encode(fileName, "UTF-8");
// 处理中文文件名的问题
fileName = new String(fileName.getBytes(StandardCharsets.UTF_8), "GBK");
response.reset();
response.setCharacterEncoding("UTF-8");
// 不同类型的文件对应不同的MIME类型
response.setContentType("application/x-msdownload");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
int bytesRead;
byte[] buffer = new byte[4096];
//重点
while ((bytesRead = bis.read(buffer)) != -1) {
// 将文件发送到客户端
bos.write(buffer, 0, bytesRead);
bos.flush();
}
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
try {
if (null != bis) {
bis.close();
}
if (null != bos) {
bos.close();
}
if (null != os) {
os.close();
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
}
生成临时文件方式下载
/**
* 测试文件上传,需要生成本地文件
*/
(value = "/getZipFiles2",method =RequestMethod.GET)
public void getZipFiles2(HttpServletResponse response) throws Exception {
// 获取三个文件的字符流
File file1 = new File("D:\\test\\1.exe");
File file2 = new File("D:\\test\\2.exe");
File file3 = new File("D:\\test\\3.exe");
BufferedInputStream bis1 = new BufferedInputStream(new FileInputStream(file1), 1024);
BufferedInputStream bis2 = new BufferedInputStream(new FileInputStream(file2), 1024);
BufferedInputStream bis3 = new BufferedInputStream(new FileInputStream(file3), 1024);
List<BufferedInputStream> fileList = new ArrayList();
fileList.add(bis1);
fileList.add(bis2);
fileList.add(bis3);
//创建zip输出流
ZipOutputStream out = new ZipOutputStream(new FileOutputStream("D:\\test\\1.zip"));
//创建缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(out);
for (int i = 0; i < fileList.size(); i++) {
out.putNextEntry(new ZipEntry(i+".exe"));
int len=-1;
//将源文件写入到zip文件中
byte[] buf = new byte[1024];
while ((len = fileList.get(i).read(buf)) != -1) {
bos.write(buf,0,len);
}
fileList.get(i).close();
}
bos.close();
// 输出流转输入流
downloadFile(response, new BufferedInputStream(new FileInputStream("D:\\test\\1.zip"), 1024), "1.zip");
}
测试
三个文件压缩后的大小大概是192MB,其实算比较大了。
我们看到总用时是8.83秒(博主多次测试,大概都是这个时间)。
直接输出流转输入流的方式下载
/**
* 测试文件上传,不需要生成本地文件
*/
(value = "/getZipFiles1",method =RequestMethod.GET)
public void getZipFiles1(HttpServletResponse response) throws Exception {
// 获取三个文件的字符流
File file1 = new File("D:\\test\\1.exe");
File file2 = new File("D:\\test\\2.exe");
File file3 = new File("D:\\test\\3.exe");
BufferedInputStream bis1 = new BufferedInputStream(new FileInputStream(file1), 1024);
BufferedInputStream bis2 = new BufferedInputStream(new FileInputStream(file2), 1024);
BufferedInputStream bis3 = new BufferedInputStream(new FileInputStream(file3), 1024);
List<BufferedInputStream> fileList = new ArrayList();
fileList.add(bis1);
fileList.add(bis2);
fileList.add(bis3);
// 压缩成一个zip
//创建一个ByteArray输出流
ByteArrayOutputStream arrayOutputStream =new ByteArrayOutputStream();
//创建zip输出流
ZipOutputStream out = new ZipOutputStream(arrayOutputStream);
//创建缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(out);
for (int i = 0; i < fileList.size(); i++) {
out.putNextEntry(new ZipEntry(i+".exe"));
int len=-1;
//将源文件写入到zip文件中
byte[] buf = new byte[1024];
while ((len = fileList.get(i).read(buf)) != -1) {
bos.write(buf,0,len);
}
fileList.get(i).close();
}
bos.close();
// 输出流转输入流
InputStreamSource inputStreamSource = new ByteArrayResource(arrayOutputStream.toByteArray());
downloadFile(response, new BufferedInputStream(inputStreamSource.getInputStream()), "1.zip");
}
测试
三个文件压缩后的大小大概是192MB,其实算比较大了。
我们看到总用时是6.29秒(博主多次测试,大概都是这个时间)。
总结
其实文件下载,没必要先生成一个本地文件。
完全可以将输出流转成输入流,或者将输入流转成输出流,直接在内存中做转换,没有中间商赚差价!
事实证明,没有中间商赚差价可以提高大概20%-30%的响应速度!
其中输入流输出流的互相转换方式,请查看: