<option id="tpnx7"><p id="tpnx7"><pre id="tpnx7"></pre></p></option>
<output id="tpnx7"></output>
<output id="tpnx7"><ruby id="tpnx7"></ruby></output>
    <output id="tpnx7"></output><acronym id="tpnx7"><button id="tpnx7"><div id="tpnx7"></div></button></acronym><code id="tpnx7"><menuitem id="tpnx7"></menuitem></code>

    首页 > 上网技巧 > 电脑小技巧 > 基于 Node.js 实现压缩和解压缩

    基于 Node.js 实现压缩和解压缩

    时间:2018-12-23 09:52 作者:QQ地带 我要评论

    压缩格式
    zip 和 gzip 是两种我们最常见到的压缩格式,当然,gzip 在 Windows 下很少有人接触。
    tar 是一种归档格式,它默认不会压缩,需要结合 gzip 来将最终的 tar 文件以 gzip 格式压缩成为一个 tar.gz 文件,通常我们会缩写为 tgz。
    为什么没有提到 rar?因为它是专利保护的算法,你可以免费获得解压工具,而压缩工具是需要付费的。所以我们一般应用场景下,很少会提供 rar 压缩文件。
    本文将分别介绍 gzip,tar,tgz 和 zip 的压缩和解压缩在 Node.js 下如何实现。
    未压缩文件库
    本文所使用的未压缩文件库来自于 urllib ,需要先 clone 它下来到?#20184;?#30446;录。
    git clone https://github.com/node-modules/urllib.git nodejs-compressing-demo
    gzip
    在 Linux 的世界,每个工具的职责会很纯粹,非常单一,如 gzip,它?#25442;?#23545;文件进行压缩,至于文件夹如何打包压缩,跟它没关系,那是 tar 要去负责的事情。
    gzip 命令行压缩一个文件
    例如我们要将 nodejs-compressing-demo/lib/urllib.js 文件进行 gzip 压缩,会得到一个 urllib.js.gz 文件,源文件会被删除。
    $ 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 命令,将文件压缩并保存为?#25105;?#25991;件:
    $ ls -l nodejs-compressing-demo/README.md
    -rw-r--r--  1 a  a  13747 Feb 12 11:27 nodejs-compressing-demo/README.md
     
    $ 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 的世界,早已有人为你准?#36127;?#36825;些基础库,我们只需要开箱即用。
    本文将会使用 compressing 模块,实现所有压缩和解压缩代码。
    为什么会选择 compressing?因为它有足够充分的代码质量和单元测试保证,处于活跃的维护状态,API 非常友好,而?#19968;?#25903;?#33267;?#24335;接口。
    Promise 接口
    const compressing = require('compressing');
     
    // 选择 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 操作。
    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。
    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 pump = require('pump');
     
    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'), 
        new compressing.gzip.FileStream(), 
        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 文件上传的示例代码,我?#24039;?#24494;?#33041;?#19968;下,就能实现 gzip 压缩然后返回。
    const pump = require('pump');
     
    class UploadFormController extends Controller {
      // ... other codes
     
      async upload() {
        const stream = await this.ctx.getFileStream();
        // 直接将压缩流?#25345;?#32473; ctx.body,实现边压缩边返回?#29287;?#24335;响应
        this.ctx.body = pump(stream, new compressing.gzip.FileStream());
      }
    }
    tar | gzip > tgz
    gzip 章节可以提前知道,tar 是负责对文件夹进?#20889;?#21253;📦的。
    例如要对 nodejs-compressing-demo 整个文件?#20889;?#21253;成一个文件发送给别人,可以通过 tar 命令完成。
    $ 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 打包出来的文件一般都比较大,因为它是未压缩的,大小跟实际文件夹总大小接近。所以我们都会在打包同时进行压缩。
    $ 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 倍大小的差异,可?#28304;?#22823;减少网络传输带宽。
    Node.js 实现 tgz
    Promise 接口
    先使用 compressing.tar.compressDir(sourceDir, targetFile) 将一个文件?#20889;?#21253;到一个 tar 文件,然后使用上文的 gzip 压缩方式,将 tar 文件压缩为 tgz 文件。
    const compressing = require('compressing');
     
    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 的编程模型,代码写起来会更加容易阅读:
    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 类,可以动态添加?#25105;?#25991;件、文件夹到一个 tar stream 对象中,非常灵活。
    const tarStream = new compressing.tar.Stream();
    // dir
    tarStream.addEntry('dir/path/to/compress');
    // file
    tarStream.addEntry('file/path/to/compress');
    // buffer
    tarStream.addEntry(buffer);
    // stream
    tarStream.addEntry(stream);
     
    const destStream = fs.createWriteStream('path/to/destination.tgz');
    pump(tarStream, new compressing.gzip.FileStream(), destStream, err => {
      if (err) {
        console.error(err);
      } else {
        console.log('success');
      }
    });
    zip
    zip 其实可以看作是 tar + gzip 的「商业化」结合,它让使用者不需要区分是压缩文件还是压缩文件夹,反正用我 zip ?#25237;?#20102;。
    使用 zip 命令行工具压缩一个文件夹的例子:
    $ 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 的过程。
    const compressing = require('compressing');
     
    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 实现的压缩和解压缩是否?#35748;?#35937;中简单??#34892;?npm 这个巨人,让我们编程也能拥有命令行工具那样简单的体验。
    无论是 Promise 接口,还是 Stream 接口,都有它最合?#23454;?#22330;景,你会选择了吗?
    到此,你拥有的压缩和解压缩能力,你能够做什么样的服务和功能呢?

    标签: Node.js
    顶一下
    (0)
    0%
    踩一下
    (0)
    0%

    Google提供的广告

    广东十分快乐开奖结果
    <option id="tpnx7"><p id="tpnx7"><pre id="tpnx7"></pre></p></option>
    <output id="tpnx7"></output>
    <output id="tpnx7"><ruby id="tpnx7"></ruby></output>
    <output id="tpnx7"></output><acronym id="tpnx7"><button id="tpnx7"><div id="tpnx7"></div></button></acronym><code id="tpnx7"><menuitem id="tpnx7"></menuitem></code>

    <option id="tpnx7"><p id="tpnx7"><pre id="tpnx7"></pre></p></option>
    <output id="tpnx7"></output>
    <output id="tpnx7"><ruby id="tpnx7"></ruby></output>
    <output id="tpnx7"></output><acronym id="tpnx7"><button id="tpnx7"><div id="tpnx7"></div></button></acronym><code id="tpnx7"><menuitem id="tpnx7"></menuitem></code>

    浙江快乐12选5软件 查一下河北十一选五的开奖公告 2019国庆节放假安排 广东时时彩怎么赚钱吗 玩通比牛牛怎样才会赢 七星彩开奖结果直播现场 腾讯彩票中秋充值 群英会分布图至 福彩走势图 河南快三下载安装 中国体彩网大乐透94期 福建时时彩历史开奖号码 打麻将技巧大全豆丁网 黑龙江快乐十分360图 龍博娱乐城压大小