90

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

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

4

12 回答 12

115

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

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

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

2020 年更新:尚未尝试,但也有存档器

热门推荐:yauzl

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

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

第二选:node-stream-zip

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

安装:

npm install --save node-stream-zip

用法:

'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));
      }
    );
  });
});

安全警告

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

您可以通过比较轻松地自行检查/\.\./.test(path.relative('./to/dir', path.resolve('./to/dir', entry.name)))

优点:(为什么我认为它是最好的?)

  • 可以解压缩普通文件(也许不是一些带有奇怪扩展名的疯狂文件)
  • 可以流式传输
  • 似乎不必加载整个 zip 来读取条目
  • 有普通 JavaScript 中的示例(未编译)
  • 不包括厨房水槽(即 url 加载、S3 或 db 层)
  • 使用流行库中的一些现有代码
  • 代码中没有太多无意义的时髦或忍者富

缺点

  • 像饥饿的河马一样吞下错误
  • 抛出字符串而不是错误(无堆栈跟踪)
  • zip.extract()似乎不起作用(因此我zip.stream()在示例中使用了)

亚军:node-unzipper

安装:

npm install --save unzipper

用法:

'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()
  });

优点

  • 似乎以与 类似的方式工作node-stream-zip,但控制较少
  • 一个更实用的分支unzip
  • 似乎是串行运行而不是并行运行

缺点

  • 厨房水槽多?仅包含大量与解压缩无关的内容
  • 读取整个文件(按块,这很好),而不仅仅是随机搜索
于 2017-10-27T04:20:26.727 回答
42

结帐adm-zip

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

该库允许您:

  • 将 zip 文件直接解压缩到磁盘或内存缓冲区
  • .zip压缩文件并将它们以格式或压缩缓冲区存储到磁盘
  • 从现有文件中更新/添加新/删除文件的内容.zip
于 2012-04-25T01:26:40.740 回答
36

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

var zlib = require('zlib');

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

    console.log(result);
});

编辑:您甚至可以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);

对于 tar 档案,有 Isaacs 的tar 模块,由 npm 使用。

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

于 2012-04-25T10:58:09.500 回答
18

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

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

var extract = require('extract-zip')
extract(zipfile, { dir: outputPath }, function (err) {
   // handle err
})
于 2016-07-04T06:00:49.337 回答
15

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

  • 遵循规范。不要扫描本地文件头。读取文件元数据的中央目录。
  • 不要阻塞 JavaScript 线程。使用和提供异步 API。
  • 控制内存使用。不要尝试一次在 RAM 中缓冲整个文件。
  • 永远不要崩溃(如果使用得当)。不要让格式错误的 zip 文件导致试图捕获错误的客户端应用程序崩溃。
  • 捕获不安全的文件名条目。如果 zip 文件条目的文件名以“/”或 /[A-Za-z]:// 开头,或者它包含“..”路径段或“\”(根据规范),则 zip 文件条目会引发错误。

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

于 2015-03-02T18:27:23.360 回答
5

我发现以下成功,适用于.zip
(此处为发布简化:没有错误检查&只是将所有文件解压缩到当前文件夹)

function DownloadAndUnzip(URL){
    var unzip = require('unzip');
    var http = require('http');
    var request = http.get(URL, function(response) {
        response.pipe(unzip.Extract({path:'./'}))
    });
}
于 2016-03-08T08:14:16.690 回答
3

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

用法很简单:

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);
    });

这是声明:

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);
};
于 2016-03-01T20:58:16.690 回答
0

下载并解压.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"
    })
  );
});
于 2020-02-06T04:10:33.610 回答
0

结帐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)
  }
}
于 2020-04-18T05:57:52.087 回答
0

如果您不需要将多个文件放入存档,而是压缩一个文件或仅压缩一个字符串内容,则可以使用zlib.deflateRaw/ 。zlib.inflateRaw

这是一个如何在 macOS/iOS 上压缩内存中的字符串并在 NodeJS 中解压缩的示例。

// Swift, macOS/iOS
let data = "HelloZip!".data(using: .utf8)!
let compressedData = (data as NSData).compressed(using: .zlib) as Data
let compressedDataAsBase64EncodedString = compressedData.base64EncodedString()
print(compressedDataAsBase64EncodedString)
// Prints: 80jNycmPyixQBAA=
// JS, AWS Lambda.
import zlib from "zlib";

const compressedDataAsBase64EncodedString = "80jNycmPyixQBAA="
const compressedData = Buffer.from(compressedDataAsBase64EncodedString, "base64")
const data = zlib.inflateRawSync(compressedData)
console.log(data.toString())
// Prints: HelloZip!
于 2021-07-14T22:47:56.453 回答
-1

另一个工作示例:

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");
于 2012-12-20T15:29:12.257 回答
-3

您也可以使用“解压缩”来简单地提取现有的 zip 文件。它适用于任何大小的文件,您需要将其添加为npm的依赖项。

fs.createReadStream(filePath).pipe(unzip.Extract({path:moveIntoFolder})).on('close', function(){
        //To do after unzip
				callback();
		});

于 2018-03-14T05:54:20.017 回答