本文发布于:2021-08-29,最后更新于:2021-09-03,如果内容失效请留言告知。
目录
[隐藏]
1 Node.js 中的 zilb 库与文件压缩
zlib 支持的三种压缩算法:gzip
、deflate
、brotli
。基础用法示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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
三种不同压缩算法的简单实现。其压缩效率如何呢?我们写个脚本来简单测试一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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 压缩的示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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 简单易用。以下为相关示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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 下使用。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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