Динамически создавать и передавать почтовый клиенту
Я использую NodeJs (w/express), и я пытаюсь передать zip файл обратно клиенту. Файлы, содержащиеся в zip, не живут в файловой системе, а динамически создаются. Я хотел бы передать содержимое файла (ов) в zip и передать zip обратно клиенту.
т.е. Я хочу, чтобы клиент получил:
tmp.zip
--> 1.txt
--> 2.txt
--> 3.txt
Где 1,2,3.txt создаются "на лету" и передаются в zip файл. Возможно ли это?
Ответы
Ответ 1
Archiver имеет метод добавления, который позволяет сохранять текст в виде файла. Чтобы "передать" эти данные пользователю, вы можете просто передать 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 файл с двумя текстовыми файлами. Пользователь, посетивший эту страницу, будет представлен приглашением загрузки файла.
Ответ 2
с помощью: 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();
});
Ответ 3
Да, это возможно. Я рекомендую взглянуть на Streams Playground, чтобы понять, как работают Node потоки.
Сжатие zip в основной библиотеке zlib, похоже, не поддерживает несколько файлов. Если вы хотите пойти с tar-gzip, вы можете использовать его с node-tar. Но если вы хотите ZIP, adm-zip выглядит как лучший вариант. Другая возможность - node-archiver.
Update:
Этот example показывает, как использовать Archiver, который поддерживает потоки. Просто замените fs.createReadStream
на потоки, которые вы создаете динамически, и output
поток для Express 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');
});