Ich möchte eine Zip-Datei aus dem Internet herunterladen und im Speicher entpacken, ohne sie in einer temporären Datei zu speichern. Wie kann ich das machen?
Folgendes habe ich versucht:
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
});
});
[BEARBEITEN] Wie vorgeschlagen, habe ich versucht, die adm-Zip-Bibliothek zu verwenden, und ich kann dies immer noch nicht zum Laufen bringen:
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
});
Sie benötigen eine Bibliothek, die Puffer verarbeiten kann. Die neueste Version von adm-Zip
Wird besorgt:
npm install adm-Zip
Meine Lösung verwendet die http.get
-Methode, da sie Buffer-Chunks zurückgibt.
Code:
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]));
}
});
});
Die Idee ist, ein Array von Puffern zu erstellen und diese am Ende zu einem neuen zu verketten. Dies liegt an der Tatsache, dass die Größe von Puffern nicht geändert werden kann.
pdate
Dies ist eine einfachere Lösung, bei der das Modul request
verwendet wird, um die Antwort in einem Puffer zu erhalten, indem encoding: null
in den Optionen. Es folgt auch Weiterleitungen und löst http/https automatisch auf.
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));
});
});
Das body
der Antwort ist ein Puffer, der direkt an AdmZip
übergeben werden kann, wodurch der gesamte Prozess vereinfacht wird.
Leider können Sie den Antwort-Stream nicht in den Entpack-Job leiten , da dies der Knoten zlib
lib zulässt. Sie müssen und zwischenspeichern Warten Sie bis zum Ende der Antwort. Ich empfehle Ihnen, die Antwort bei großen Dateien auf einen fs
-Stream zu leiten, da Sie sonst Ihren Speicherplatz blitzschnell ausfüllen!
Ich verstehe nicht ganz, was Sie versuchen, aber imho das ist der beste Ansatz . Sie sollten Ihre Daten nur so lange im Speicher behalten, wie Sie sie wirklich benötigen und dann zum csv-Parser.
Wenn Sie alle Ihre Daten im Speicher behalten möchten, können Sie die csv-Parsermethode fromPath
durch from
ersetzen, die stattdessen einen Puffer benötigt und in getData direkt unzipped
zurückgibt.
Sie können AMDZip
(wie @mihai sagte) anstelle von node-Zip
Verwenden, achten Sie nur darauf, dass AMDZip
noch nicht in npm veröffentlicht ist. Sie benötigen also:
$ npm install git://github.com/cthackers/adm-Zip.git
N.B. Annahme: Die Zip-Datei enthält nur eine Datei
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)
})
})
Wenn Sie unter MacOS oder Linux arbeiten, können Sie mit dem Befehl unzip
aus stdin
entpacken.
In diesem Beispiel lese ich die Zip-Datei aus dem Dateisystem in ein Buffer
-Objekt, aber es funktioniert auch mit einer heruntergeladenen Datei:
// 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();
Welches ist eigentlich nur die Knoten-Version von:
cat test.Zip | unzip -p /dev/stdin test.js
[~ # ~] edit [~ # ~] : Es ist erwähnenswert, dass dies nicht funktioniert, wenn die Eingabe-Zip zu groß ist, um in einer eingelesen zu werden Stück von stdin. Wenn Sie größere Dateien lesen müssen und Ihre Zip-Datei nur eine Datei enthält, können Sie funzip anstelle von unzip
verwenden:
var unzip = spawn("funzip");
Wenn Ihre Zip-Datei mehrere Dateien enthält (und die gewünschte Datei nicht die erste ist), muss ich leider sagen, dass Sie Pech haben. Das Entpacken muss in der Datei .Zip
Gesucht werden, da Zip-Dateien nur ein Container sind und das Entpacken möglicherweise nur die letzte Datei darin entpackt. In diesem Fall müssen Sie die Datei vorübergehend speichern ( Node-Temp ist praktisch).
Vor zwei Tagen das Modul node-Zip
wurde veröffentlicht, ein Wrapper für die reine JavaScript-Version von Zip: JSZip .
var NodeZip = require('node-Zip')
, Zip = new NodeZip(zipBuffer.toString("base64"), { base64: true })
, unzipped = Zip.files["your-text-file.txt"].data;