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



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

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

现在是

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

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

但是,大多数库似乎要么完全糟糕,要么最近几个月前才提交。这是非常令人担忧的...所以我浏览了几个解压缩库,阅读了他们的文档,并尝试了他们的例子来试图弄清楚 WTF。例如,我尝试过这些:

  • 乔什沃尔夫/yauzl
  • 安泰尔/node-stream-zip
  • 宗森/node-unzipper
  • 埃文·奥克斯菲尔德/node-unzip
  • 斯图克/jszip
  • 克里斯科瓦尔/zip

2020年更新:还没有尝试过,但也有存档器

热门推荐:yauzl

非常适合完全下载的文件。对于流媒体来说不是很好。

有据可查。效果很好。意义。

第二顺位:node-stream-zip

安泰尔的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 或数据库层)
  • 使用流行库中的一些现有代码
  • 代码中没有太多毫无意义的时髦或忍者

缺点

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

亚军:节点解压缩器

安装:

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分支
  • 似乎以串行而不是并行方式运行

缺点

  • 厨房水槽多吗?只包含大量与解压缩无关的东西
  • 读取整个文件(按块,这很好),而不仅仅是随机查找

Checkout adm-zip。

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

该库允许您:

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

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(使用请求):

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.

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

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

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

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

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

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

我期待了很长时间,没有找到简单的工作示例,但基于这些答案,我创建了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);
};

结帐枪压缩文件

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

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

在提取文件的完整性方面遇到问题,唯一适合我的解决方案是使用 https://www.npmjs.com/package/decompress

另一个工作示例:

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

如果您不需要将多个文件放入存档,而是压缩一个文件或仅压缩一个字符串内容,则可以使用 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!

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

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

最新更新