在 Node.js 跨平台中下载和解压缩文件的最简单方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10308110/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 15:35:58  来源:igfitidea点击:

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

node.jszip

提问by Lance Pollard

Just looking for a simple solution to downloading and unzipping .zipor .tar.gzfiles in Node.js on any operating system.

只是寻找一个简单的解决方案,以下载并解压缩.zip.tar.gz任何操作系统上的Node.js的文件。

Not sure if this is built in or I have to use a separate library. Any ideas? Looking for just a couple lines of code so when the next zip file comes that I want to download in node, it's a no brainer. Feel like this should be easy and/or built in, but I can't find anything. Thanks!

不确定这是内置的还是我必须使用单独的库。有任何想法吗?只查找几行代码,以便当我想在 node 中下载下一个 zip 文件时,这很容易。感觉这应该很容易和/或内置,但我找不到任何东西。谢谢!

采纳答案by bryanmac

Checkout adm-zip.

结帐adm-zip

ADM-ZIP is a pure JavaScript implementation for zip data compression for NodeJS.

ADM-ZIP 是用于 NodeJS zip 数据压缩的纯 JavaScript 实现。

The library allows you to:

  • decompress zip files directly to disk or in-memory buffers
  • compress files and store them to disk in .zipformat or in compressed buffers
  • update content of/add new/delete files from an existing .zip

该库允许您:

  • 将 zip 文件直接解压缩到磁盘或内存缓冲区
  • 压缩文件并将它们以.zip格式或压缩缓冲区存储到磁盘
  • 从现有文件中更新/添加新文件/删除文件的内容 .zip

回答by CoolAJ86

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

现在是 2017 年(准确地说是 10 月 26 日)。

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".

对于像解压缩这样古老而普遍的技术,我希望存在一个相当流行、成熟的 node.js 解压缩库,它“停滞”和“未维护”,因为它是“完整的”。

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:

然而,大多数库要么看起来很糟糕,要么在几个月前才提交过。这很令人担忧……所以我浏览了几个解压缩库,阅读了他们的文档,并尝试了他们的示例来试图找出 WTF。例如,我试过这些:

Top Recommendation: yauzl

热门推荐: yauzl

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

非常适合完全下载的文件。不太适合流式传输。

Well documented. Works well. Makes sense.

有据可查。效果很好。说得通。

2nd Pick: node-stream-zip

第二选择: node-stream-zip

antelle's node-stream-zipseems to be the best

antelle的node-stream-zip似乎是最好的

Install:

安装:

npm install --save node-stream-zip

Usage:

用法:

'use strict';

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
    mkdirp(path.dirname(pathname, function (err) {
      stream.pipe(fs.createWriteStream(pathname));
    });
  });
});

Security Warning:

安全警告

Not sure if this checks entry.namefor maliciously crafted paths that would resolve incorrectly (such as ../../../fooor /etc/passwd).

不确定这是否会检查entry.name会错误解析的恶意制作的路径(例如../../../foo/etc/passwd)。

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

您可以通过比较轻松地自行检查/\.\./.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
  • 可以解压缩普通文件(也许不是一些带有奇怪扩展名的疯狂文件)
  • 可以流
  • 似乎不必加载整个 zip 来读取条目
  • 有普通 JavaScript 示例(未编译)
  • 不包括厨房接收器(即 url 加载、S3 或 db 层)
  • 使用流行库中的一些现有代码
  • 代码中没有太多毫无意义的时髦或忍者富翁

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)
  • 像饥饿的河马一样吞下错误
  • 抛出字符串而不是错误(没有堆栈跟踪)
  • zip.extract()似乎不起作用(因此我zip.stream()在我的例子中使用了)

Runner up: node-unzipper

亚军: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
  • 似乎以类似的方式工作node-stream-zip,但控制较少
  • 一个更实用的 fork unzip
  • 似乎是串行运行而不是并行运行

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
  • 厨房水槽多吗?只包含大量与解压无关的内容
  • 读取整个文件(按块,很好),而不仅仅是随机查找

回答by Linus Gustav Larsson Thiel

Node has builtin support for gzip and deflate via the zlib module:

Node 通过zlib 模块内置了对 gzip 和 deflate 的支持:

var zlib = require('zlib');

zlib.gunzip(gzipBuffer, function(err, result) {
    if(err) return console.error(err);

    console.log(result);
});

Edit:You can even pipethe data directly through e.g. Gunzip(using request):

编辑:您甚至可以pipe直接通过例如Gunzip(使用request)数据:

var request = require('request'),
    zlib = require('zlib'),
    fs = require('fs'),
    out = fs.createWriteStream('out');

// Fetch http://example.com/foo.gz, gunzip it and store the results in 'out'
request('http://example.com/foo.gz').pipe(zlib.createGunzip()).pipe(out);

For tar archives, there is Isaacs' tar module, which is used by npm.

对于 tar 档案,有 NPM 使用的 Isaacs 的tar 模块

Edit 2:Updated answer as zlibdoesn't support the zipformat. This will only work for gzip.

编辑 2:更新了zlib不支持zip格式的答案。这仅适用于gzip.

回答by andrewrk

yauzlis a robust library for unzipping. Design principles:

yauzl是一个强大的解压库。设计原则:

  • Follow the spec. Don't scan for local file headers. Read the central directory for file metadata.
  • Don't block the JavaScript thread. Use and provide async APIs.
  • Keep memory usage under control. Don't attempt to buffer entire files in RAM at once.
  • Never crash (if used properly). Don't let malformed zip files bring down client applications who are trying to catch errors.
  • Catch unsafe filenames entries. A zip file entry throws an error if its file name starts with "/" or /[A-Za-z]:// or if it contains ".." path segments or "\" (per the spec).
  • 遵循规范。不要扫描本地文件头。读取文件元数据的中央目录。
  • 不要阻塞 JavaScript 线程。使用并提供异步 API。
  • 控制内存使用。不要试图一次在 RAM 中缓冲整个文件。
  • 永不崩溃(如果使用得当)。不要让格式错误的 zip 文件导致试图捕获错误的客户端应用程序瘫痪。
  • 捕获不安全的文件名条目。如果 zip 文件条目以“/”或 /[A-Za-z]:// 开头,或者包含“..”路径段或“\”(根据规范),则会引发错误。

Currently has 97% test coverage.

目前有 97% 的测试覆盖率。

回答by Simon Hutchison

I tried a few of the nodejs unzip libraries including adm-zip and unzip, then settled on extract-zip which is a wrapper around yauzl. Seemed the simplest to implement.

我尝试了一些 nodejs 解压缩库,包括 adm-zip 和 unzip,然后选择了 extract-zip,它是 yauzl 的包装器。似乎最容易实现。

https://www.npmjs.com/package/extract-zip

https://www.npmjs.com/package/extract-zip

var extract = require('extract-zip')
extract(zipfile, { dir: outputPath }, function (err) {
   // handle err
})

回答by Adam

I was looking forward this for a long time, and found no simple working example, but based on these answers I created the downloadAndUnzip()function.

我期待了很长时间,并没有找到简单的工作示例,但是基于这些答案我创建了该downloadAndUnzip()函数。

The usage is quite simple:

用法很简单:

downloadAndUnzip('http://your-domain.com/archive.zip', 'yourfile.xml')
    .then(function (data) {
        console.log(data); // unzipped content of yourfile.xml in root of archive.zip
    })
    .catch(function (err) {
        console.error(err);
    });

And here is the declaration:

这是声明:

var AdmZip = require('adm-zip');
var request = require('request');

var downloadAndUnzip = function (url, fileName) {

    /**
     * Download a file
     * 
     * @param url
     */
    var download = function (url) {
        return new Promise(function (resolve, reject) {
            request({
                url: url,
                method: 'GET',
                encoding: null
            }, function (err, response, body) {
                if (err) {
                    return reject(err);
                }
                resolve(body);
            });
        });
    };

    /**
     * Unzip a Buffer
     * 
     * @param buffer
     * @returns {Promise}
     */
    var unzip = function (buffer) {
        return new Promise(function (resolve, reject) {

            var resolved = false;

            var zip = new AdmZip(buffer);
            var zipEntries = zip.getEntries(); // an array of ZipEntry records

            zipEntries.forEach(function (zipEntry) {
                if (zipEntry.entryName == fileName) {
                    resolved = true;
                    resolve(zipEntry.getData().toString('utf8'));
                }
            });

            if (!resolved) {
                reject(new Error('No file found in archive: ' + fileName));
            }
        });
    };


    return download(url)
        .then(unzip);
};

回答by Mtl Dev

I found success with the following, works with .zip
(Simplified here for posting: no error checking & just unzips all files to current folder)

我发现以下成功,与 .zip 一起使用
(这里简化了发布:没有错误检查&只是将所有文件解压缩到当前文件夹)

function DownloadAndUnzip(URL){
    var unzip = require('unzip');
    var http = require('http');
    var request = http.get(URL, function(response) {
        response.pipe(unzip.Extract({path:'./'}))
    });
}

回答by Basic Primitives Support

Another working example:

另一个工作示例:

var zlib = require('zlib');
var tar = require('tar');
var ftp = require('ftp');

var files = [];

var conn = new ftp();
conn.on('connect', function(e) 
{
    conn.auth(function(e) 
    {
        if (e)
        {
            throw e;
        }
        conn.get('/tz/tzdata-latest.tar.gz', function(e, stream) 
        {
            stream.on('success', function() 
            {
                conn.end();

                console.log("Processing files ...");

                for (var name in files)
                {
                    var file = files[name];

                    console.log("filename: " + name);
                    console.log(file);
                }
                console.log("OK")
            });
            stream.on('error', function(e) 
            {
                console.log('ERROR during get(): ' + e);
                conn.end();
            });

            console.log("Reading ...");

            stream
            .pipe(zlib.createGunzip())
            .pipe(tar.Parse())
            .on("entry", function (e) 
            {    
                var filename = e.props["path"];
                console.log("filename:" + filename);
                if( files[filename] == null )
                {
                    files[filename] = "";
                }
                e.on("data", function (c) 
                {
                    files[filename] += c.toString();
                })    
            });
        });
    });
})
.connect(21, "ftp.iana.org");

回答by arnaudjnn

Checkout gunzip-file

结帐gunzip 文件

import gunzip from 'gunzip-file';

const unzipAll = async () => {
  try {
    const compFiles = fs.readdirSync('tmp')
    await Promise.all(compFiles.map( async file => {
      if(file.endsWith(".gz")){
        gunzip(`tmp/${file}`, `tmp/${file.slice(0, -3)}`)
      }
    }));
  }
  catch(err) {
    console.log(err)
  }
}

回答by vitaliytv

Download and extract for .tar.gz:

下载并解压缩.tar.gz

const https = require("https");
const tar = require("tar");

https.get("https://url.to/your.tar.gz", function(response) {
  response.pipe(
    tar.x({
      strip: 1,
      C: "some-dir"
    })
  );
});