2013-11-20 8 views
18

Ich benutze NodeJs (w/express) und ich versuche, eine ZIP-Datei zurück auf den Client zu streamen. Die in der ZIP enthaltenen Dateien leben nicht im Dateisystem, sondern werden dynamisch erstellt. Ich möchte den Inhalt der Datei (en) in die zip streamen und den zip zurück zum Client streamen.Dynamisch erstellen und streamen ZIP zu Client

I.E. Ich möchte der Kunde erhalten:

tmp.zip 
--> 1.txt 
--> 2.txt 
--> 3.txt 

Wo 1,2,3.txt on the fly erstellt und auf die ZIP-Datei gestreamt. Ist das möglich?

+0

Es ist klar, nicht das, was Sie durch "um die ZIP-Datei gestreamt" bedeuten ... welche Datei wird der HTTP-Client empfangen schlussendlich? die Zip-Datei? oder eine Verkettung der drei Textdateien? –

+0

Entschuldigung, Formatierung wurde durcheinander gebracht, hoffentlich ist das klarer. Ich möchte einen Zip zurücksenden. 2 Hauptprobleme versuche ich meinen Kopf einzuwickeln. 1. Wie streame ich die Zip zurück, 2. wie streame ich meine 3 Dateien in eine Zip-Datei, die ich zurück streame. I.E. Ich habe Beispiele für das Streaming einer Zip-Datei gesehen, die bereits im Dateisystem vorhanden ist, aber es gibt kein Beispiel dafür, wie Sie das tun, wenn Sie die Dateien im Fly erstellen. – lostintranslation

+0

konnten Sie eine Problemumgehung für dasselbe finden? –

Antwort

31

Archiver hat eine Append-Methode, mit der Sie Text als Datei speichern können. Um diese Daten an den Benutzer zu "streamen", können Sie einfach eine Pipeline an das HTTP-Antwortobjekt anschließen.

var Http = require('http'); 
var Archiver = require('archiver'); 

Http.createServer(function (request, response) { 
    // Tell the browser that this is a zip file. 
    response.writeHead(200, { 
     'Content-Type': 'application/zip', 
     'Content-disposition': 'attachment; filename=myFile.zip' 
    }); 

    var zip = Archiver('zip'); 

    // Send the file to the page output. 
    zip.pipe(response); 

    // Create zip with some files. Two dynamic, one static. Put #2 in a sub folder. 
    zip.append('Some text to go in file 1.', { name: '1.txt' }) 
     .append('Some text to go in file 2. I go in a folder!', { name: 'somefolder/2.txt' }) 
     .file('staticFiles/3.txt', { name: '3.txt' }) 
     .finalize(); 

}).listen(process.env.PORT); 

Dadurch wird eine Zip-Datei mit den zwei Textdateien erstellt. Dem Benutzer, der diese Seite besucht, wird eine Aufforderung zum Herunterladen von Dateien angezeigt.

+0

Was passiert nun mit der Zip-Datei, die nach dem Anhängen aller Dateien erstellt wurde? Ich habe eine ähnliche Situation, in der ich meinem Benutzer erlauben möchte, mehrere Dateien gleichzeitig herunterzuladen. Da kann ich sie nicht zusammen mit Antwort senden.Ich muss eine temporäre Zip erstellen und verschieben Sie Dateien darin und dann dienen Sie es und löschen Sie es dann, um Speicherplatz zu speichern. Also, erstellt Archiver eine temporäre Zip-Datei oder eine permanente? – DeadMan

+1

Ich glaube nicht, dass die Zip-Datei "provoziert" wird, da das Dienstprogramm die Zip-Datei erstellt, die diese Bits in den Stream schreibt, was die Antwort auf die http-Anfrage wäre. Ich bin mir sicher, dass es für eine Minute im RAM (oder der Auslagerungsdatei, wenn es ziemlich groß ist) weiterlebt, bis die Datei an den Browser gesendet wird, der sie dann in den temporären Dateien des Endbenutzers speichert, bis sie gespeichert wird ein Ort. Ich habe diesen Code jetzt seit ein paar Monaten live laufen lassen und meine Speicherplatzbelegung ist gleich geblieben, so dass es keine temporären Reißverschlüsse gibt, die Sie löschen müssen. – CuddleBunny

+1

Dieses Beispiel aus dem Archiver Repo hat mir geholfen. https://github.com/ctalkington/node-archiver/blob/master/examples/express.js – eephillip

3

Ja, es ist möglich. Ich empfehle, einen Blick auf Streams Playground zu werfen, um ein Gefühl dafür zu bekommen, wie Knoten-Streams funktionieren.

Die ZIP-Komprimierung in der zlib-Kernbibliothek scheint nicht mehrere Dateien zu unterstützen. Wenn Sie mit tar-gzip arbeiten möchten, können Sie es mit node-tar tarieren. Aber wenn Sie ZIP wollen, sieht adm-zip wie die beste Option aus. Eine andere Möglichkeit ist node-archiver.

Update:

Diese example zeigt, wie Archiver verwenden, die Streams unterstützt. Ersetzen Sie einfach fs.createReadStream durch die Streams, die Sie dynamisch erstellen, und senden Sie output an Express res statt an fs.createWriteStream.

var fs = require('fs'); 

var archiver = require('archiver'); 

var output = fs.createWriteStream(__dirname + '/example-output.zip'); 
var archive = archiver('zip'); 

output.on('close', function() { 
    console.log('archiver has been finalized and the output file descriptor has closed.'); 
}); 

archive.on('error', function(err) { 
    throw err; 
}); 

archive.pipe(output); 

var file1 = __dirname + '/fixtures/file1.txt'; 
var file2 = __dirname + '/fixtures/file2.txt'; 

archive 
    .append(fs.createReadStream(file1), { name: 'file1.txt' }) 
    .append(fs.createReadStream(file2), { name: 'file2.txt' }); 

archive.finalize(function(err, bytes) { 
    if (err) { 
    throw err; 
    } 

    console.log(bytes + ' total bytes'); 
}); 
+0

Verwirrender Teil ist ich habe keine Idee, wie man adm-zip (oder irgendein anderes Zipmodul) benutzt, um die Zip in einen Strom und nicht in eine Akte auf dem Dateisystem zu schreiben. Ganz zu schweigen von der Stream-Datei an die Zip, die dann zum Client streamt. – lostintranslation

+0

Ich habe ein Beispiel für Sie hinzugefügt. – dankohn

+0

dieses Beispiel gesehen. Ich kann immer noch nicht bekommen, was ich brauche, um zu arbeiten. Ich erstelle file1 und file2 im laufenden Betrieb (sie sind nicht im Dateisystem). Wenn ich Daten für diese Dateien erstelle, möchte ich sie zum Stream hinzufügen. Aber ich möchte, dass es gezippt wird, wenn ich es hinzufüge. – lostintranslation

5

Lösung mit: Express.js, wait.for, zip-stream

app.get('/api/box/:box/:key/download', function (req, res) { 

    var wait = require('wait.for'); 

    var items = wait.for(function (next) { 
     BoxItem.find({box: req.Box}).exec(next) 
    }); 

    res.set('Content-Type', 'application/zip'); 
    res.set('Content-Disposition', 'attachment; filename=' + req.Box.id + '.zip'); 

    var ZipStream = require('zip-stream'); 
    var zip = new ZipStream(); 

    zip.on('error', function (err) { 
     throw err; 
    }); 

    zip.pipe(res); 

    items.forEach(function (item) { 

     wait.for(function (next) { 

      var path = storage.getItemPath(req.Box, item); 
      var source = require('fs').createReadStream(path); 

      zip.entry(source, { name: item.name }, next); 
     }) 

    }); 

    zip.finalize(); 

}); 
+0

@ZachB Ich glaube nicht, dass es langsamer ist, weil die Module zwischengespeichert werden. –

Verwandte Themen