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

时间:2012-04-25 01:13:06

标签: node.js zip

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

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

12 个答案:

答案 0 :(得分:45)

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

对于一种古老而普遍的技术,例如解压缩,我希望存在一个相当流行的,成熟的node.js解压缩库,这是一个停滞不前的"并且"没有维护"因为它是完整的"。

然而,大多数图书馆看起来要么是完全可怕的,要么就像几个月前一样提交。这非常令人担忧......所以我已经浏览了几个解压缩库,阅读了他们的文档,并尝试了他们的例子来试图找出WTF。例如,我尝试过这些:

最高建议:yauzl

适用于完全下载的文件。不如流媒体好。

记录良好。效果很好。有道理。

第二次选择:node-stream-zip

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

安装:

npm install --save node-stream-zip

用法:

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

安全警告

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

您可以通过比较/\.\./.test(path.relative('./to/dir', path.resolve('./to/dir', entry.name)))

轻松自行检查

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

  • 可以解压缩普通文件(也许不是一些带有奇怪扩展名的疯狂文件)
  • 可以流式传输
  • 似乎无需加载整个zip来读取条目
  • 在普通JavaScript中有示例(未编译)
  • 不包括厨房水槽(即网址加载,S3或数据库层)
  • 使用来自热门图书馆的一些现有代码
  • 在代码中没有太多无谓的行家或忍者foo

<强>缺点

  • 像饥饿的河马一样吞下错误
  • 抛出字符串而不是错误(没有堆栈跟踪)
  • 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
  • 的功能更强大的分支
  • 似乎是串行而不是并行运行

<强>缺点

  • 厨房水槽多少?只包含大量与解压缩无关的内容
  • 读取整个文件(按块,这很好),而不仅仅是随机搜索

答案 1 :(得分:29)

结帐adm-zip

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

  

图书馆允许您:

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

答案 2 :(得分:28)

Node通过zlib module

内置了对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 module,由npm使用。

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

答案 3 :(得分:13)

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

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

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

答案 4 :(得分:10)

我尝试了一些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
})

答案 5 :(得分: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);
};

答案 6 :(得分:2)

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

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

答案 7 :(得分:0)

另一个工作示例:

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

答案 8 :(得分:0)

结帐gunzip-file

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

答案 9 :(得分: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!

答案 10 :(得分:-1)

下载并解压缩.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"
    })
  );
});

答案 11 :(得分:-2)

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

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