Simplest way to download and unzip files in Node.js cross-platform?

前端 未结 11 1578
一个人的身影
一个人的身影 2020-11-30 23:44

Just looking for a simple solution to downloading and unzipping .zip or .tar.gz files in Node.js on any operating system.

Not sure if this

11条回答
  •  星月不相逢
    2020-12-01 00:10

    It's 2017 (October 26th, to be exact).

    For an ancient and pervasive technology such as unzip I would expect there to exist a fairly popular, mature node.js unzip library that is "stagnant" and "unmaintained" because it is "complete".

    However, most libraries appear either to be completely terrible or to have commits recently as just a few months ago. This is quite concerning... so I've gone through several unzip libraries, read their docs, and tried their examples to try to figure out WTF. For example, I've tried these:

    • thejoshwolfe/yauzl
    • antelle/node-stream-zip
    • ZJONSSON/node-unzipper
    • EvanOxfeld/node-unzip
    • Stuk/jszip
    • kriskowal/zip

    Update 2020: Haven't tried it yet, but there's also archiver

    Top Recommendation: yauzl

    Works great for completely downloaded file. Not as great for streaming.

    Well documented. Works well. Makes sense.

    2nd Pick: node-stream-zip

    antelle's node-stream-zip seems to be the best

    Install:

    npm install --save node-stream-zip
    

    Usage:

    'use strict';
    
    var fs = require('fs');
    var StreamZip = require('node-stream-zip');
    
    var zip = new StreamZip({
      file: './example.zip'
    , storeEntries: true
    });
    
    zip.on('error', function (err) { console.error('[ERROR]', err); });
    
    zip.on('ready', function () {
      console.log('All entries read: ' + zip.entriesCount);
      //console.log(zip.entries());
    });
    
    zip.on('entry', function (entry) {
      var pathname = path.resolve('./temp', entry.name);
      if (/\.\./.test(path.relative('./temp', pathname))) {
          console.warn("[zip warn]: ignoring maliciously crafted paths in zip file:", entry.name);
          return;
      }
    
      if ('/' === entry.name[entry.name.length - 1]) {
        console.log('[DIR]', entry.name);
        return;
      }
    
      console.log('[FILE]', entry.name);
      zip.stream(entry.name, function (err, stream) {
        if (err) { console.error('Error:', err.toString()); return; }
    
        stream.on('error', function (err) { console.log('[ERROR]', err); return; });
    
        // example: print contents to screen
        //stream.pipe(process.stdout);
    
        // example: save contents to file
        fs.mkdir(
          path.dirname(pathname),
          { recursive: true },
          function (err) {
            stream.pipe(fs.createWriteStream(pathname));
          }
        );
      });
    });
    

    Security Warning:

    Not sure if this checks entry.name for maliciously crafted paths that would resolve incorrectly (such as ../../../foo or /etc/passwd).

    You can easily check this yourself by comparing /\.\./.test(path.relative('./to/dir', path.resolve('./to/dir', entry.name))).

    Pros: (Why do I think it's the best?)

    • can unzip normal files (maybe not some crazy ones with weird extensions)
    • can stream
    • seems to not have to load the whole zip to read entries
    • has examples in normal JavaScript (not compiled)
    • doesn't include the kitchen sink (i.e. url loading, S3, or db layers)
    • uses some existing code from a popular library
    • doesn't have too much senseless hipster or ninja-foo in the code

    Cons:

    • Swallows errors like a hungry hippo
    • Throws strings instead of errors (no stack traces)
    • zip.extract() doesn't seem to work (hence I used zip.stream() in my example)

    Runner up: node-unzipper

    Install:

    npm install --save unzipper
    

    Usage:

    'use strict';
    
    var fs = require('fs');
    var unzipper = require('unzipper');
    
    fs.createReadStream('./example.zip')
      .pipe(unzipper.Parse())
      .on('entry', function (entry) {
        var fileName = entry.path;
        var type = entry.type; // 'Directory' or 'File'
    
        console.log();
        if (/\/$/.test(fileName)) {
          console.log('[DIR]', fileName, type);
          return;
        }
    
        console.log('[FILE]', fileName, type);
    
        // TODO: probably also needs the security check
    
        entry.pipe(process.stdout/*fs.createWriteStream('output/path')*/);
        // NOTE: To ignore use entry.autodrain() instead of entry.pipe()
      });
    

    Pros:

    • Seems to work in a similar manner to node-stream-zip, but less control
    • A more functional fork of unzip
    • Seems to run in serial rather than in parallel

    Cons:

    • Kitchen sink much? Just includes a ton of stuff that's not related to unzipping
    • Reads the whole file (by chunk, which is fine), not just random seeks

提交回复
热议问题