Подтвердить что ты не робот

Как загрузить и распаковать zip файл в памяти в NodeJs?

Я хочу загрузить 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
     });
  });

[EDIT] Как, предположил, я попытался использовать библиотеку 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
    });
4b9b3361

Ответ 1

  • Вам нужна библиотека, которая может обрабатывать буферы. Последняя версия adm-zip будет выполнять:

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

  • В моем решении используется метод http.get, так как он возвращает куски буфера.

код:

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

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

var options = {
    host: url.parse(file_url).host,
    port: 80,
    path: url.parse(file_url).pathname
};

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

    res.on('data', function(chunk) {

            data.push(chunk);
            dataLen += chunk.length;

        }).on('end', function() {
            var buf = new Buffer(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++)
                console.log(zip.readAsText(zipEntries[i])); 
        });
});

Идея состоит в том, чтобы создать массив буферов и объединить их в новый в конце. Это связано с тем, что буферы не могут быть изменены.

Ответ 2

К сожалению, вы не можете передать поток ответов в задание на распаковку, поскольку node zlib lib позволяет вам делать, вам нужно кэшировать и ждать окончания ответа. Я предлагаю вам передать ответ потоку fs в случае больших файлов, иначе вы полностью заполните свою память миганием!

Я не совсем понимаю, что вы пытаетесь сделать, но imho это лучший подход. Вы должны хранить свои данные в памяти только в тот момент, когда вам это действительно нужно, а затем поток csv parser.

Если вы хотите сохранить все свои данные в памяти, вы можете заменить метод ssv parser fromPath на from, который вместо этого берет буфер, а в getData возвращает unzipped

Вы можете использовать AMDZip (как указано @mihai) вместо node-zip, просто обратите внимание, потому что AMDZip пока не опубликован в npm, поэтому вам нужно:

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

N.B. Предположение: 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)
  })
})

Ответ 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();

На самом деле это версия node:

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

EDIT. Стоит отметить, что это не сработает, если входной почтовый индекс слишком велик, чтобы его можно было читать в одном фрагменте из stdin. Если вам нужно прочитать большие файлы, а ваш zip файл содержит только один файл, вы можете использовать funzip вместо unzip:

var unzip = spawn("funzip");

Если ваш zip файл содержит несколько файлов (и файл, который вы хотите, не первый), я боюсь сказать, что вам не повезло. Unzip нужно искать в файле .zip, так как zip файлы - это просто контейнер, и unzip может просто распаковать последний файл в нем. В этом случае вам нужно временно сохранить файл (node-temp).

Ответ 4

Два дня назад был выпущен модуль 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;

Ответ 5

var fs = require ('fs); var unzip = require ('unzip');

//распаковать a.zip в текущий словарь

fs.createReadStream('./path/a.zip'). pipe (unzip.Extract({path: './path/'}));

Я использовал unzip-модуль, и он работал.