【语言】压缩 20M 文件从 30 秒到 2 秒的全部过程?
有一个需求需要将前端传过来的 10 张照片,然后后端进行处理以后压缩成一个压缩包通过网络流传输出去。之前没有接触过用 Java 压缩文件的,所以就直接上网找了一个例子改了一下用了,改完以后也能使用,但是随着前端所传图片的大小越来越大的时候,耗费的时间也在急剧增加,最后测了一下压缩 20M 的文件竟然需要 30 秒的时间。压缩文件的代码如下。
public static void zipFileNoBuffer() {
File zipFile = new File(ZIP_FILE);
try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile))) {
//开始时间
long beginTime = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
try (InputStream input = new FileInputStream(JPG_FILE)) {
zipOut.putNextEntry(new ZipEntry(FILE_NAME + i));
int temp = 0;
while ((temp = input.read()) != -1) {
zipOut.write(temp);
}
}
}
printInfo(beginTime);
} catch (Exception e) {
e.printStackTrace();
}
}
复制代码这里找了一张 2M 大小的图片,并且循环十次进行测试。打印的结果如下,时间大概是 30 秒。
fileSize:20M
consum time:29599
优化过程 - 从 30 秒到 2 秒
进行优化首先想到的是利用缓冲区 BufferInputStream。在 FileInputStream 中 read() 方法每次只读取一个字节。源码中也有说明。
/Reads a byte of data from this input stream. This method blocks
if no input is yet available.
@return the next byte of data, or <code>-1</code> if the end of the
file is reached.
@exception IOException if an I/O error occurs./
public native int read() throws IOException;
复制代码这是一个调用本地方法与原生操作系统进行交互,从磁盘中读取数据。每读取一个字节的数据就调用一次本地方法与操作系统交互,是非常耗时的。例如我们现在有 30000 个字节的数据,如果使用 FileInputStream 那么就需要调用 30000 次的本地方法来获取这些数据,而如果使用缓冲区的话(这里假设初始的缓冲区大小足够放下 30000 字节的数据)那么只需要调用一次就行。因为缓冲区在第一次调用 read() 方法的时候会直接从磁盘中将数据直接读取到内存中。随后再一个字节一个字节的慢慢返回。
BufferedInputStream 内部封装了一个 byte 数组用于存放数据,默认大小是 8192
优化过后的代码如下:
public static void zipFileBuffer() {
File zipFile = new File(ZIP_FILE);
try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(zipOut)) {
//开始时间
long beginTime = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(JPG_FILE))) {
zipOut.putNextEntry(new ZipEntry(FILE_NAME + i));
int temp = 0;
while ((temp = bufferedInputStream.read()) != -1) {
bufferedOutputStream.write(temp);
}
}
}
printInfo(beginTime);
} catch (Exception e) {
e.printStackTrace();
}
}
复制代码输出
——Buffer
fileSize:20M
consum time:1808