web-dev-qa-db-fra.com

Créez et diffusez Zip de manière dynamique vers le client

J'utilise NodeJs (w/express) et j'essaie de diffuser un fichier Zip sur le client. Les fichiers contenus dans le Zip ne vivent pas sur le système de fichiers, ils sont plutôt créés dynamiquement. Je voudrais diffuser le (s) contenu (s) de fichier sur le Zip et diffuser le Zip sur le client.

C'EST À DIRE. Je souhaite que le client reçoive:

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

Où 1,2,3.txt sont créés à la volée et diffusés dans le fichier Zip. Est-ce possible?

30
lostintranslation

Archiveur a une méthode d'ajout qui vous permet d'enregistrer du texte sous forme de fichier. Pour "diffuser" ces données à l'utilisateur, vous pouvez simplement diriger vers l'objet de réponse HTTP.

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

Cela va créer un fichier Zip avec les deux fichiers texte. L'utilisateur visitant cette page recevra une invite de téléchargement de fichier.

49
CuddleBunny

solution avec: 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();

});
6
slava

Oui c'est possible. Je recommande de jeter un œil à Streams Playground pour avoir une idée du fonctionnement de Node Streams.

La compression Zip dans la bibliothèque principale de zlib ne semble pas prendre en charge plusieurs fichiers. Si vous voulez utiliser tar-gzip, vous pouvez le tarer avec node-tar . Mais si vous voulez Zip, adm-Zip ressemble à la meilleure option. Une autre possibilité est node-archiver .

Mise à jour:

Cette exemple montre comment utiliser Archiver, qui prend en charge les flux. Remplacez simplement fs.createReadStream avec les flux que vous créez dynamiquement, et avez output stream vers res d'Express plutôt que vers 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');
});
5
Dan Kohn