50

インターネットから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
     });
  });

[編集] 提案されたように、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
    });
4

4 に答える 4

93

バッファを処理できるライブラリが必要です。の最新バージョンは次のadm-zipことを行います。

npm install adm-zip

http.getBuffer チャンクを返すため、私のソリューションではこのメソッドを使用します。

コード:

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

アイデアは、バッファの配列を作成し、それらを最後に新しいものに連結することです。これは、バッファーのサイズを変更できないためです。

アップデート

これは、オプションで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));
  });
});

応答のbodyは、 に直接渡すことができるバッファであり、AdmZipプロセス全体を簡素化します。

于 2012-04-30T22:59:57.220 に答える
5

残念ながら、ノード lib で許可されているように、応答ストリームを unzip ジョブにパイプzlibすることはできません。キャッシュして、応答の最後を待つ必要があります。大きなファイルの場合は、応答をストリームにパイプすることをお勧めしfsます。そうしないと、メモリが瞬く間にいっぱいになってしまいます。

あなたがやろうとしていることを完全には理解していませんが、これが最善のアプローチです。本当に必要なときだけデータをメモリに保持してから、 csv パーサーストリームする必要があります。

すべてのデータをメモリに保持したい場合は、csv パーサー メソッドfromPathfrom、代わりにバッファーを受け取り、getData で直接返すメソッドに置き換えることができます。unzipped

AMDZipの代わりに (@mihai が言ったように) を使用できますが、 npm でまだ公開されていないnode-zipため注意してください。AMDZip

$ npm install git://github.com/cthackers/adm-zip.git

注意: zip ファイルには 1 つのファイルしか含まれていません

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)
  })
})
于 2012-05-01T22:11:20.490 に答える
3

MacOS または Linux を使用している場合は、unzipコマンドを使用して から解凍できますstdin

この例では、ファイル システムからオブジェクトに zip ファイルを読み込んでいBufferますが、ダウンロードしたファイルでも同様に機能します。

// Get a Buffer with the zip content
var fs = require("fs")
  , zip = fs.readFileSync(__dirname + "/test.zip");


// Now the actual unzipping:
var spawn = require('child_process').spawn
  , fileToExtract = "test.js"
    // -p tells unzip to extract to stdout
  , unzip = spawn("unzip", ["-p", "/dev/stdin", fileToExtract ])
  ;

// Write the Buffer to stdin
unzip.stdin.write(zip);

// Handle errors
unzip.stderr.on('data', function (data) {
  console.log("There has been an error: ", data.toString("utf-8"));
});

// Handle the unzipped stdout
unzip.stdout.on('data', function (data) {
  console.log("Unzipped file: ", data.toString("utf-8"));
});

unzip.stdin.end();

これは実際には次のノードバージョンです。

cat test.zip | unzip -p /dev/stdin test.js

編集:入力zipが大きすぎて標準入力から1つのチャンクで読み取ることができない場合、これは機能しないことに注意してください。より大きなファイルを読み取る必要があり、zip ファイルに含まれるファイルが 1 つだけの場合は、次の代わりにfunzipunzipを使用できます。

var unzip = spawn("funzip");

zip ファイルに複数のファイルが含まれている場合 (そして、必要なファイルが最初のファイルではない場合)、残念ながらあなたは運が悪いと言えます。.zipzip ファイルは単なるコンテナーであり、unzip はファイル内の最後のファイルを解凍するだけなので、解凍はファイルをシークする必要があります。その場合、ファイルを一時的に保存する必要があります ( node-tempが便利です)。

于 2012-04-30T21:49:47.283 に答える
1

2 日前にモジュールnode-zipがリリースされました。これは、JavaScript のみのバージョンの Zip: JSZipのラッパーです。

var NodeZip = require('node-zip')
  , zip = new NodeZip(zipBuffer.toString("base64"), { base64: true })
  , unzipped = zip.files["your-text-file.txt"].data;
于 2012-04-30T23:20:11.757 に答える