基于Node.js实现压缩和解压缩的方法

压缩格式

zip 和 gzip 是两种我们最常见到的压缩格式,当然,gzip 在 Windows 下很少有人接触。

tar 是一种归档格式,它认不会压缩,需要结合 gzip 来将最终的 tar 文件以 gzip 格式压缩成为一个 tar.gz 文件,通常我们会缩写为 tgz。

为什么没有提到 rar?因为它是专利保护的算法,你可以免费获得解压工具,而压缩工具是需要付费的。所以我们一般应用场景下,很少会提供 rar 压缩文件

本文将分别介绍 gzip,tar,tgz 和 zip 的压缩和解压缩在 Node.js 下如何实现。

未压缩文件

本文所使用的未压缩文件库来自于 urllib ,需要先 clone 它下来到指定目录。

代码如下:
https://github.com/node-modules/urllib.git nodejs-compressing-demo

gzip

在 Linux 的世界,每个工具的职责会很纯粹,非常单一,如 gzip,它只会对文件进行压缩,至于文件夹如何打包压缩,跟它没关系,那是 tar 要去负责的事情。

gzip 命令行压缩一个文件

例如我们要将 nodejs-compressing-demo/lib/urllib.js 文件进行 gzip 压缩,会得到一个 urllib.js.gz 文件,源文件会被删除

rush:bash;"> $ ls -l nodejs-compressing-demo/lib/urllib.js -rw-r--r-- 1 a a 31318 Feb 12 11:27 nodejs-compressing-demo/lib/urllib.js

$ gzip nodejs-compressing-demo/lib/urllib.js

$ ls -l nodejs-compressing-demo/lib/urllib.js.gz
-rw-r--r-- 1 a a 8909 Feb 12 11:27 nodejs-compressing-demo/lib/urllib.js.gz

还原压缩文件

$ gunzip nodejs-compressing-demo/lib/urllib.js.gz

文件大小从 31318 字节减少到 8909 字节,超过 3.5 倍的压缩效果

还可以通过 pipe 方式,结合 cat 命令,将文件压缩并保存为任意文件

$ cat nodejs-compressing-demo/README.md | gzip > README.md.gz

$ ls -l README.md.gz
-rw-r--r-- 1 a a 4903 Feb 12 11:50 README.md.gz

Node.js 实现 gzip

当然,我们不会真的从零开始实现一个 gzip 算法和工具,在 Node.js 的世界,早已有人为你准备好这些基础库,我们只需要开箱即用。

本文将会使用 compressing 模块,实现所有压缩和解压缩代码

为什么会选择 compressing?因为它有足够充分的代码质量和单元测试保证,处于活跃的维护状态,API 非常友好,而且还支持流式接口。

Promise 接口

// 选择 gzip 格式,然后调用 compressFile 方法
compressing.gzip.compressFile('nodejs-compressing-demo/lib/urllib.js','nodejs-compressing-demo/lib/urllib.js.gz')
.then(() => {
console.log('success');
})
.catch(err => {
console.error(err);
});

// 解压缩是反响过程,接口都统一为 uncompress
compressing.gzip.uncompress('nodejs-compressing-demo/lib/urllib.js.gz','nodejs-compressing-demo/lib/urllib.js2')
.then(() => {
console.log('success');
})
.catch(err => {
console.error(err);
});

结合 async/await 的编程模型,代码写起来就是一个普通的异步 io 操作。

rush:js;"> const compressing = require('compressing');

async function main() {
try {
await compressing.gzip.compressFile('nodejs-compressing-demo/lib/urllib.js','nodejs-compressing-demo/lib/urllib.js.gz');
console.log('success');
} catch (err) {
console.error(err);
}

// 解压缩
try {
await compressing.gzip.uncompress('nodejs-compressing-demo/lib/urllib.js.gz','nodejs-compressing-demo/lib/urllib.js2');
console.log('success');
} catch (err) {
console.error(err);
}
}

main();

Stream 接口

需要特别注意的是,使用 Stream 模式编程,需要处理每个 stream 的 error 事件,并且要手动销毁所有 stream

rush:js;"> fs.createReadStream('nodejs-compressing-demo/lib/urllib.js') .on('error',handleError) .pipe(new compressing.gzip.FileStream()) // It's a transform stream .on('error',handleError) .pipe(fs.createWriteStream('nodejs-compressing-demo/lib/urllib.js.gz2')) .on('error',handleError);

// 解压缩,就是 pipe 的方向倒转过来
fs.createReadStream('nodejs-compressing-demo/lib/urllib.js.gz2')
.on('error',handleError)
.pipe(new compressing.gzip.Uncompressstream()) // It's a transform stream
.on('error',handleError)
.pipe(fs.createWriteStream('nodejs-compressing-demo/lib/urllib.js3'))
.on('error',handleError);

根据官方的Backpressuring in Streams 推荐,我们应该使用 pump 模块来配合 Stream 模式编程,由 pump 来完成这些 Stream 的清理工作。

const source = fs.createReadStream('nodejs-compressing-demo/lib/urllib.js');
const target = fs.createWriteStream('nodejs-compressing-demo/lib/urllib.js.gz2');

pump(source,new compressing.gzip.FileStream(),target,err => {
if (err) {
console.error(err);
} else {
console.log('success');
}
});

// 解压缩
pump(fs.createReadStream('nodejs-compressing-demo/lib/urllib.js.gz2'),fs.createWriteStream('nodejs-compressing-demo/lib/urllib.js3'),err => {
if (err) {
console.error(err);
} else {
console.log('success');
}
});

Stream 接口的优势

Stream 接口看起来比 Promise 接口复杂多了,为何还会有这种应用场景呢?

其实在 HTTP 服务领域,Stream 模型会有更大的优势,因为 HTTP 请求本身就是一个 Request Stream,如要将一个上传文件以 gzip 压缩返回,使用 Stream 接口不需要将上传文件保存到本地磁盘,而是直接消费这个文件流。

使用 egg 文件上传的示例代码 ,我们稍微改造一下,就能实现 gzip 压缩然后返回。

rush:js;"> const pump = require('pump');

class UploadFormController extends Controller {
// ... other codes

async upload() {
const stream = await this.ctx.getFileStream();
// 直接将压缩流赋值给 ctx.body,实现边压缩边返回的流式响应
this.ctx.body = pump(stream,new compressing.gzip.FileStream());
}
}

tar | gzip > tgz

gzip 章节可以提前知道,tar 是负责对文件夹进行打包:package:的。

例如要对 nodejs-compressing-dem o 整个文件夹打包成一个文件发送给别人,可以通过 tar 命令完成。

rush:bash;"> $ tar -c -f nodejs-compressing-demo.tar nodejs-compressing-demo/

$ ls -l nodejs-compressing-demo.tar
-rw-r--r-- 1 a a 206336 Feb 12 14:01 nodejs-compressing-demo.tar

如大家所见,tar 打包出来的文件一般都比较大,因为它是未压缩的,大小跟实际文件夹总大小接近。所以我们都会在打包同时进行压缩。

rush:bash;"> $ tar -c -z -f nodejs-compressing-demo.tgz nodejs-compressing-demo/

$ ls -l nodejs-compressing-demo.tgz
-rw-r--r-- 1 a a 39808 Feb 12 14:07 nodejs-compressing-demo.tgz

tar 和 tgz 超过 5 倍大小的差异,可以大大减少网络传输带宽。

Node.js 实现 tgz

Promise 接口

先使用 compressing.tar.compressDir(sourceDir,targetFile) 将一个文件夹打包到一个 tar 文件,然后使用上文的 gzip 压缩方式,将 tar 文件压缩为 tgz 文件

compressing.tar.compressDir('nodejs-compressing-demo','nodejs-compressing-demo.tar')
.then(() => {
return compressing.gzip.compressFile('nodejs-compressing-demo.tar','nodejs-compressing-demo.tgz');
});
.then(() => {
console.log('success');
})
.catch(err => {
console.error(err);
});

// 解压缩
compressing.gzip.uncompress('nodejs-compressing-demo.tgz','nodejs-compressing-demo.tar')
.then(() => {
return compressing.tar.uncompress('nodejs-compressing-demo.tar','nodejs-compressing-demo2');
});
.then(() => {
console.log('success');
})
.catch(err => {
console.error(err);
});

结合 async/await 的编程模型,代码写起来会更加容易阅读:

rush:js;"> const compressing = require('compressing');

async function main() {
try {
await compressing.tar.compressDir('nodejs-compressing-demo','nodejs-compressing-demo.tar');
await compressing.gzip.compressFile('nodejs-compressing-demo.tar','nodejs-compressing-demo.tgz');
console.log('success');
} catch (err) {
console.error(err);
}

// 解压缩
try {
await compressing.gzip.uncompress('nodejs-compressing-demo.tgz','nodejs-compressing-demo.tar');
await compressing.tar.uncompress('nodejs-compressing-demo.tar','nodejs-compressing-demo2');
console.log('success');
} catch (err) {
console.error(err);
}
}

main();

Stream 接口

通过 compressing.tar.Stream 类,可以动态添加任意文件文件夹到一个 tar stream 对象中,非常灵活。

const destStream = fs.createWriteStream('path/to/destination.tgz');
pump(tarStream,destStream,err => {
if (err) {
console.error(err);
} else {
console.log('success');
}
});

zip

zip 其实可以看作是 tar + gzip 的「商业化」结合,它让使用者不需要区分是压缩文件还是压缩文件夹,反正用我 zip 就对了。

使用 zip 命令行工具压缩一个文件夹的例子:

rush:bash;"> $ zip -r nodejs-compressing-demo.zip nodejs-compressing-demo/ adding: nodejs-compressing-demo/ (stored 0%) adding: nodejs-compressing-demo/test/ (stored 0%) ... adding: nodejs-compressing-demo/.travis.yml (deflated 36%)

$ ls -l nodejs-compressing-demo.*
-rw-r--r-- 1 a a 206336 Feb 12 14:06 nodejs-compressing-demo.tar
-rw-r--r-- 1 a a 39808 Feb 12 14:07 nodejs-compressing-demo.tgz
-rw-r--r-- 1 a a 55484 Feb 12 14:34 nodejs-compressing-demo.zip

通过 tgz 和 zip 文件大小对比,可以看出认的压缩参数下,gzip 的效果会比 zip 好。

Node.js 实现 zip

实现代码跟 tar 类似,只不过认是压缩的,不需要再添加 gzip 的过程。

compressing.zip.compressDir('nodejs-compressing-demo','nodejs-compressing-demo.zip')
.then(() => {
console.log('success');
})
.catch(err => {
console.error(err);
});

// 解压缩
compressing.zip.uncompress('nodejs-compressing-demo.zip','nodejs-compressing-demo3')
.then(() => {
console.log('success');
})
.catch(err => {
console.error(err);
});

总结

基于 Node.js 实现的压缩和解压缩是否比想象中简单?感谢 npm 这个巨人,让我们编程也能拥有命令行工具那样简单的体验。

无论是 Promise 接口,还是 Stream 接口,都有它最合适的场景,你会选择了吗?

到此,你拥有的压缩和解压缩能力,你能够做什么样的服务和功能呢?

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程之家。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


这篇文章主要介绍“基于nodejs的ssh2怎么实现自动化部署”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“基于nodejs...
本文小编为大家详细介绍“nodejs怎么实现目录不存在自动创建”,内容详细,步骤清晰,细节处理妥当,希望这篇“nodejs怎么实现目录不存在自动创建”文章能帮助大...
这篇“如何把nodejs数据传到前端”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这...
本文小编为大家详细介绍“nodejs如何实现定时删除文件”,内容详细,步骤清晰,细节处理妥当,希望这篇“nodejs如何实现定时删除文件”文章能帮助大家解决疑惑...
这篇文章主要讲解了“nodejs安装模块卡住不动怎么解决”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来...
今天小编给大家分享一下如何检测nodejs有没有安装成功的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文...
本篇内容主要讲解“怎么安装Node.js的旧版本”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎...
这篇“node中的Express框架怎么安装使用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家...
这篇文章主要介绍“nodejs如何实现搜索引擎”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“nodejs如何实现搜索引擎...
这篇文章主要介绍“nodejs中间层如何设置”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“nodejs中间层如何设置”文...
这篇文章主要介绍“nodejs多线程怎么实现”,在日常操作中,相信很多人在nodejs多线程怎么实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法...
这篇文章主要讲解了“nodejs怎么分布式”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“nodejs怎么分布式”...
本篇内容介绍了“nodejs字符串怎么转换为数组”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情...
这篇文章主要介绍了nodejs如何运行在php服务器的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇nodejs如何运行在php服务器文章都...
本篇内容主要讲解“nodejs单线程如何处理事件”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“nodejs单线程如何...
这篇文章主要介绍“nodejs怎么安装ws模块”,在日常操作中,相信很多人在nodejs怎么安装ws模块问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法...
本篇内容介绍了“怎么打包nodejs代码”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!
本文小编为大家详细介绍“nodejs接收到的汉字乱码怎么解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“nodejs接收到的汉字乱码怎么解决”文章能帮助大家解...
这篇“nodejs怎么同步删除文件”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇...
今天小编给大家分享一下nodejs怎么设置淘宝镜像的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希