使用 JavaScript 实现流式下载增量的 zip 压缩包

在最近的一个前端应用开发中,需要处理用户上传的大量图片,并将处理后的图片以下载形式保存到用户设备。由于图片数量众多,单独下载每个文件既不方便也不实用,因此选择将这些图片压缩成一个 zip 文件后进行下载。

面对大量文件处理的需求,生成的 zip 压缩包体积可能非常庞大。如果在全部处理完毕后才开始下载,将需要在内存中临时存储巨大的文件数据,这不仅占用大量内存,还可能导致应用在导出瞬间响应缓慢。为了优化这一过程,采用了流式写入技术,这样可以避免内存中出现巨大的临时变量,也可以让用户实时看到下载进度。

实现方法

关键技术

主要使用了两个 JavaScript 库来实现此功能:

  1. streamsaver —— 用于创建可以直接写入文件的下载流。
  2. @zip.js/zip.js —— 用于动态创建 zip 文件,并将文件内容以 Uint8ArrayBlob 的形式写入压缩包。

实现步骤

以下是实现该功能的具体代码步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { ZipWriter, Uint8ArrayReader, BlobReader } from '@zip.js/zip.js';
import { createWriteStream } from 'streamsaver';

// 创建 ZIP 文件的写入流
const fileStream = createWriteStream("pack.zip");
const writer = fileStream.getWriter();
const zipWriter = new ZipWriter(new WritableStream({
write(chunk) {
return writer.write(chunk);
},
close() {
writer.close();
}
}));

// 向 ZIP 文件中写入文件 bytes
await zipWriter.add("bytes.bin", new Uint8ArrayReader(bytes));
// 或写入一个 blob
await zipWriter.add("blob.bin", new BlobReader(blob));

// 关闭写入流,完成下载
await zipWriter.close();

在将所有输出打包成单个压缩文件的前提下,使用这种方法不仅减少了内存占用,还改善了用户的下载体验,使其在下载过程中可以观察到实时进度。这种方法对于处理大量数据或需要长时间运行的前端应用来说,是一个非常有效的解决方案。