目录
[隐藏]
1 Node.js 中的 zilb 库与文件压缩
zlib 支持的三种压缩算法:gzip
、deflate
、brotli
。基础用法示例:
import zlib from 'zlib'; function zip(str, encoding = 'gzip') { str = typeof str === 'string' ? str : JSON.stringify(str); if (encoding === 'deflate') return zlib.deflateSync(str); if (encoding === 'brotli') return zlib.brotliCompressSync(str); return zlib.gzipSync(str); } function upzip(buffer, encoding = 'gzip') { return new Promise((resolve) => { buffer instanceof Buffer ? buffer : Buffer.from(buffer); if (encoding === 'gzip') { // const buf = zlib.gunzipSync(buffer); zlib.gunzip(buffer, (error, buf) => { resolve({ error, buffer: error ? null : buf }); }); } else if (encoding === 'deflate') { // const buf = zlib.inflateSync(buffer); zlib.inflate(buffer, (error, buf) => { resolve({ error, buffer: error ? null : buf }); }); } else if (encoding === 'brotli') { // const buf = zlib.brotliDecompressSync(buffer); zlib.brotliDecompress(buffer, (error, buf) => { resolve({ error, buffer: error ? null : buf }); }); } }); }
上面是 gzip
、deflate
、brotli
三种不同压缩算法的简单实现。其压缩效率如何呢?我们写个脚本来简单测试一下:
function genRandomStrs(length = 100, range = 32) { const list = []; for (let i = 0; i < length; i++) { list.push(String.fromCharCode(Math.ceil(Math.random() * range) + 32)); } return list.join(''); } async function zlibTest() { const charsRange = [5, 15, 30, 90]; const testDataLens = [100, 1_000, 10_000, 100_000]; // 1_000_000 const algorithmList = ['gzip', 'deflate', 'brotli']; const stats = {}; for (const range of charsRange) { const item = stats[`charsRange-` + range] = {}; for (const len of testDataLens) { const testStr = genRandomStrs(len, range); if (!item.size) item.size = []; item.size.push(len); for (const algo of algorithmList) { const buf = zip(testStr, algo); const cLen = buf.toString().length; const rate = Math.ceil(100 * cLen / len) + '%'; if (!item[algo]) item[algo] = []; item[algo].push(rate); // const d = await upzip(buf, algo); } } } console.log(stats); } zlibTest();
可以看到:
- 字符范围较小(重复度高)时,压缩效率依次为:
brotli
>deflate
>gzip
- 相同条件下,文本越大(重复字符越多),压缩率越高
- 在文本范围较大时,压缩效率基本一致。
简单来说就是重复字符串越多,可压缩率越高。如果条件允许,选择 brotli
是较为合适的,但 gzip 在各场景下具有更好的通用性支持。
2 浏览器中使用第三方库 pako
实现 zip 压缩
一个支持浏览器中使用的 zlib
实现库,声称比 Node.JS 12、14 自带的 zlib
库都要快。
以下为一个在浏览器端实现文本 zip、unzip 压缩的示例。
import pako from 'pako'; // 也可以通过在页面中直接加载 CDN 的脚本测试 // <script src="https://cdn.staticfile.org/pako/1.0.10/pako.min.js"></script> function gzip(str) { if (!str) return ''; if (typeof str !== str) str = JSON.stringfiy(str); // 返回值为 utf16 编码,浏览器中对该编码字符串的读写可能会出现异常 // 这里也可以用 pako.zip,它是对 pako.deflate 的一个包装方法 const compressed = pako.deflate(str, {to: 'string'}); // 转换为 base64 编码 constt strBase64 = btoa(compressed); return strBase64; } function unGzip(strBase64) { // 也可以用 pako.ungzip,它们执行的是相同的方法逻辑 return pako.inflate(atob(strBase64), {to: 'string'}); }
更多细节可查阅其官方文档:http://nodeca.github.io/pako/
3 使用 compressing 实现文件夹的压缩与解压缩
上面分析了基于 zlib
的文件压缩方案。对于文件夹的压缩,则涉及对目录及子目录的文件遍历和全部文件归档,需要处理的细节较多。我们可以借助第三方工具包来简单的实现相关需求。
compressing
是一个相对比较流行的 Node.js 压缩与解压缩工具包,其提供的 API 简单易用。以下为相关示例:
import compressing from 'compressing'; // zip 压缩一个目录 compressing.zip.compressDir('./lzwme-test', 'lzwme-test.zip').then(() => console.log('success')).catch(e => console.error(e.stack)); // zip 解压 compressing.zip.uncompress('lzwme-test.zip', './lzwme-test').then(() => console.log('success')).catch(e => console.error(e.stack)); // tar 目录归档 compressing.tar.compressDir('./lzwme-test', 'lzwme-test.tar').then(() => console.log('success')).catch(e => console.error(e.stack)); // tar 归档解压 compressing.tar.uncompress('lzwme-test.tar', './lzwme-test').then(() => console.log('success')).catch(e => console.error(e.stack)); // gzip 压缩 compressing.gzip.compressFile('./lzwme-test.tar', 'lzwme-test.tar.gz').then(() => console.log('success')).catch(e => console.error(e.stack)); // gzip 解压 compressing.gzip.uncompress('lzwme-test.tar.gz', './lzwme-test').then(() => console.log('success')).catch(e => console.error(e.stack)); // tgz 压缩与解压(先 tar 对文件夹归档,再 gzip 对 tar 文件压缩) compressing.tgz.compressDir('./lzwme-test', 'lzwme-test.tar.gz').then(() => console.log('success')).catch(e => console.error(e.stack)); // tgz 解压 compressing.tgz.uncompress('lzwme-test.tar.gz', './lzwme-test').then(() => console.log('success')).catch(e => console.error(e.stack));
4. 使用 JSZip
实现浏览器中的 zip 文件压缩
JSZip
支持在纯浏览器端实现文件的 zip 压缩与下载。其 API 也简单易懂。其也支持 Node.js 下使用。
示例:
import JSZip from 'jszip'; const zip = new JSZip(); zip.file("Hello.txt", "Hello World\n"); const img = zip.folder("images"); img.file("smile.gif", imgData, {base64: true}); zip.generateAsync({type:"blob"}) .then(function(content) { // see FileSaver.js saveAs(content, "example.zip"); });
更为详细的用法可参考其官方示例:https://stuk.github.io/jszip/documentation/examples.html