javascript - principiantes - ¿Cómo descargar y descomprimir un archivo zip en la memoria en NodeJs?
node js download file (5)
Hace dos días se lanzó el módulo node-zip
, que es un contenedor para la versión JavaScript de Zip: JSZip .
var NodeZip = require(''node-zip'')
, zip = new NodeZip(zipBuffer.toString("base64"), { base64: true })
, unzipped = zip.files["your-text-file.txt"].data;
Quiero descargar un archivo comprimido de Internet y descomprimirlo en la memoria sin guardarlo en un archivo temporal. ¿Cómo puedo hacer esto?
Esto es lo que intenté:
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] Como sugerí, intenté usar la biblioteca adm-zip y todavía no puedo hacer que esto funcione:
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
});
Lamentablemente, no puede canalizar la secuencia de respuesta en el trabajo de descompresión, ya que el nodo zlib
lib le permite hacerlo, debe almacenar en caché y esperar al final de la respuesta. Te sugiero que canalices la respuesta a una secuencia fs
en el caso de archivos grandes, de lo contrario llenarás por completo tu memoria en un abrir y cerrar de ojos.
No entiendo completamente lo que intentas hacer, pero este es el mejor enfoque . Debería mantener sus datos en la memoria solo el tiempo que realmente los necesita , y luego transmitirlos al analizador csv .
Si desea mantener todos sus datos en la memoria, puede reemplazar el método del analizador csv de la fromPath
con from
que toma un búfer en su lugar y en getData return directamente unzipped
Puede usar AMDZip
(como dijo @mihai) en lugar de node-zip
, solo preste atención porque AMDZip
aún no se ha publicado en npm, por lo que necesita:
$ npm install git://github.com/cthackers/adm-zip.git
NB Supuesto: el archivo zip contiene solo un archivo
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)
})
})
Si está en MacOS o Linux, puede usar el comando descomprimir para descomprimir de stdin
.
En este ejemplo, estoy leyendo el archivo zip del sistema de archivos en un objeto Buffer
pero también funciona con un archivo descargado:
// 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();
Que en realidad es solo la versión del nodo de:
cat test.zip | unzip -p /dev/stdin test.js
EDITAR : Vale la pena señalar que esto no funcionará si el archivo zip de entrada es demasiado grande para ser leído en un fragmento de stdin. Si necesita leer archivos más grandes, y su archivo zip contiene solo un archivo, puede usar funzip lugar de unzip
:
var unzip = spawn("funzip");
Si su archivo comprimido contiene varios archivos (y el archivo que desea no es el primero) me temo que no tiene suerte. Descomprimir debe buscar en el archivo .zip
ya que los archivos zip son solo un contenedor, y descomprimir solo puede descomprimir el último archivo en él. En ese caso, debe guardar el archivo temporalmente ( node-temp es útil).
var fs = require (''fs); var unzip = require (''descomprimir'');
// descomprimir a.zip en el diccionario actual
fs.createReadStream (''./ path / a.zip''). pipe (descomprimir.Extract ({ruta: ''./path/''}));
Usé el módulo de descompresión, y funcionó.
Necesita una biblioteca que pueda manejar búferes. La última versión de
adm-zip
hará:npm install git://github.com/cthackers/adm-zip.git
Mi solución usa el método
http.get
, ya que devuelve fragmentos de Buffer.
Código:
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]));
});
});
La idea es crear una matriz de búferes y concatenarlos en uno nuevo al final. Esto se debe al hecho de que los buffers no pueden redimensionarse.