如何在NodeJs中下载和解压缩内存中的zip文件?

pat*_*rit 42 javascript zip zlib unzip node.js

我想从互联网上下载一个zip文件并将其解压缩到内存而不保存到临时文件.我怎样才能做到这一点?

这是我尝试过的:

var url = 'http://bdn-ak.bloomberg.com/precanned/Comdty_Calendar_Spread_Option_20120428.txt.zip';

var request = require('request'), fs = require('fs'), zlib = require('zlib');

  request.get(url, function(err, res, file) {
     if(err) throw err;
     zlib.unzip(file, function(err, txt) {
        if(err) throw err;
        console.log(txt.toString()); //outputs nothing
     });
  });
Run Code Online (Sandbox Code Playgroud)

[编辑]正如,建议,我尝试使用adm-zip库,我仍然无法使这项工作:

var ZipEntry = require('adm-zip/zipEntry');
request.get(url, function(err, res, zipFile) {
        if(err) throw err;
        var zip = new ZipEntry();
        zip.setCompressedData(new Buffer(zipFile.toString('utf-8')));
        var text = zip.getData();
        console.log(text.toString()); // fails
    });
Run Code Online (Sandbox Code Playgroud)

mih*_*hai 68

您需要一个可以处理缓冲区的库.最新版本adm-zip将做:

npm install adm-zip
Run Code Online (Sandbox Code Playgroud)

我的解决方案使用该http.get方法,因为它返回缓冲区块.

码:

var file_url = 'http://notepad-plus-plus.org/repository/7.x/7.6/npp.7.6.bin.x64.zip';

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

http.get(file_url, function(res) {
  var data = [], dataLen = 0; 

  res.on('data', function(chunk) {
    data.push(chunk);
    dataLen += chunk.length;

  }).on('end', function() {
    var buf = Buffer.alloc(dataLen);

    for (var i = 0, len = data.length, pos = 0; i < len; i++) { 
      data[i].copy(buf, pos); 
      pos += data[i].length; 
    } 

    var zip = new AdmZip(buf);
    var zipEntries = zip.getEntries();
    console.log(zipEntries.length)

    for (var i = 0; i < zipEntries.length; i++) {
      if (zipEntries[i].entryName.match(/readme/))
        console.log(zip.readAsText(zipEntries[i]));
    }
  });
});
Run Code Online (Sandbox Code Playgroud)

我们的想法是创建一个缓冲区数组,并在最后将它们连接成一个新缓冲区.这是因为缓冲区无法调整大小.

更新

这是一个更简单的解决方案request,通过设置encoding: null选项,使用模块在缓冲区中获取响应.它还遵循重定向并自动解析http/https.

var file_url = 'https://github.com/mihaifm/linq/releases/download/3.1.1/linq.js-3.1.1.zip';

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

request.get({url: file_url, encoding: null}, (err, res, body) => {
  var zip = new AdmZip(body);
  var zipEntries = zip.getEntries();
  console.log(zipEntries.length);

  zipEntries.forEach((entry) => {
    if (entry.entryName.match(/readme/i))
      console.log(zip.readAsText(entry));
  });
});
Run Code Online (Sandbox Code Playgroud)

body响应的是,可以直接传递到缓冲器AdmZip,简化了整个过程.

  • 不适用于github标签的拉链 - 错误无效或不支持的zip格式.找不到END标头 (10认同)
  • 我使用`axios`来发出请求,如果你将[responseType](https://github.com/axios/axios#request-config)设置为''arraybuffer',它可以选择将整个东西下载为ArrayBuffer `.然后你可以将`response.data`直接传递给AdmZip (3认同)
  • 我只想强调,简单地使用`npm install adm-zip`安装adm-zip将不起作用,因为只有github上的最新版本支持缓冲区. (2认同)
  • npm的最新版本支持缓冲区. (2认同)

kil*_*anc 6

可悲的是,你不能响应流进入解压工作作为节点zlibLIB允许你这样做,你必须缓存并等待响应的结束.我建议你fs在大文件的情况下将响应传递给一个流,否则你会在瞬间完全填满你的记忆!

我不完全理解你想要做什么,但是imho这是最好的方法.您应该只在您真正需要它的时候将数据保存在内存中,然后流式传输到csv解析器.

如果您想保留在内存中的所有数据可以更换CSV解析器方法fromPathfrom,需要一个缓冲区,而不是直接在返回的getDataunzipped

您可以使用AMDZip(如@mihai所说)代替node-zip,只需注意,因为AMDZip尚未在npm中发布,因此您需要:

$ npm install git://github.com/cthackers/adm-zip.git
Run Code Online (Sandbox Code Playgroud)

NB假设:zip文件只包含一个文件

var request = require('request'),
    fs = require('fs'),
    csv = require('csv')
    NodeZip = require('node-zip')

function getData(tmpFolder, url, callback) {
  var tempZipFilePath = tmpFolder + new Date().getTime() + Math.random()
  var tempZipFileStream = fs.createWriteStream(tempZipFilePath)
  request.get({
    url: url,
    encoding: null
  }).on('end', function() {
    fs.readFile(tempZipFilePath, 'base64', function (err, zipContent) {
      var zip = new NodeZip(zipContent, { base64: true })
      Object.keys(zip.files).forEach(function (filename) {
        var tempFilePath = tmpFolder + new Date().getTime() + Math.random()
        var unzipped = zip.files[filename].data
        fs.writeFile(tempFilePath, unzipped, function (err) {
          callback(err, tempFilePath)
        })
      })
    })
  }).pipe(tempZipFileStream)
}

getData('/tmp/', 'http://bdn-ak.bloomberg.com/precanned/Comdty_Calendar_Spread_Option_20120428.txt.zip', function (err, path) {
  if (err) {
    return console.error('error: %s' + err.message)
  }
  var metadata = []
  csv().fromPath(path, {
    delimiter: '|',
    columns: true
  }).transform(function (data){
    // do things with your data
    if (data.NAME[0] === '#') {
      metadata.push(data.NAME)
    } else {
      return data
    }
  }).on('data', function (data, index) {
    console.log('#%d %s', index, JSON.stringify(data, null, '  '))
  }).on('end',function (count) {
    console.log('Metadata: %s', JSON.stringify(metadata, null, '  '))
    console.log('Number of lines: %d', count)
  }).on('error', function (error) {
    console.error('csv parsing error: %s', error.message)
  })
})
Run Code Online (Sandbox Code Playgroud)