Динамически создавать и передавать zip на клиент

Я использую NodeJ (w/express), и я пытаюсь передать файл zip обратно клиенту. Файлы, содержащиеся в zip-архиве, не живут в файловой системе, а создаются динамически. Я хотел бы направить содержимое файла (ов) в zip-архив и отправить его обратно клиенту.

IE я хочу чтобы клиент получил:

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

Где 1,2,3.txt создаются на лету и передаются в zip-файл. Это возможно?

3 ответа

Решение

В архиваторе есть метод добавления, который позволяет сохранять текст в виде файла. Чтобы "передать" эти данные пользователю, вы можете просто направить объект 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);

Это создаст zip-файл с двумя текстовыми файлами. Пользователю, посещающему эту страницу, будет предложено приглашение к загрузке файла.

Решение с помощью: 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();

});

Да, это возможно. Я рекомендую взглянуть на Streams Playground, чтобы понять, как работает Node Streams.

Сжатие zip в базовой библиотеке zlib не поддерживает несколько файлов. Если вы хотите использовать tar-gzip, вы можете использовать tar с помощью node-tar. Но если вы хотите ZIP, adm-zip выглядит как лучший вариант. Другая возможность - это узел-архиватор.

Обновить:

В этом примере показано, как использовать Archiver, который поддерживает потоки. Просто заменить fs.createReadStream с потоками, которые вы создаете динамически, и имеют output поток в экспресс res а не 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');
});

Отправка zip-файла в виде двоичных данных с помощью expressjs и node-zip:

app.get("/multipleinzip", (req, res) => {
    var zip = new require('node-zip')();
    var csv1 = "a,b,c,d,e,f,g,h\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8";
    zip.file('test1.file', csv1);
    var csv2 = "z,w,x,d,e,f,g,h\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8";
    zip.file('test2.file', csv2);
    var csv3 = "q,w,e,d,e,f,g,h\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8";
    zip.file('test3.file', csv3);
    var csv4 = "t,y,u,d,e,f,g,h\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8\n1,2,3,4,5,6,7,8";
    zip.file('test4.file', csv4);
    var data = zip.generate({base64:false,compression:'DEFLATE'});
    console.log(data); // ugly data
    res.type("zip")
    res.send(new Buffer(data, 'binary'));
})

Создание ссылки для скачивания zip-файла. Получить данные и преобразовать ответ в буфер массива с помощью ->

    //get the response from fetch as arrayBuffer...
    var data = response.arrayBuffer();

    const blob = new Blob([data]);
    const fileName = `${filename}.${extension}`;
    
    if (navigator.msSaveBlob) {
      // IE 10+
      navigator.msSaveBlob(blob, fileName);
    } else {
      const link = document.createElement('a');
      // Browsers that support HTML5 download attribute
      if (link.download !== undefined) {
        const url = URL.createObjectURL(blob);
        link.setAttribute('href', url);
        link.setAttribute('download', fileName);
        link.style.visibility = 'hidden';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
      }
    }
Другие вопросы по тегам