node.js 动态创建 zip 并将其流式传输到客户端
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20107303/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Dynamically create and stream zip to client
提问by lostintranslation
I am using NodeJs (w/express) and I am trying to stream a zip file back to the client. The files contained in the zip do not live on the file system, rather they are created dynamically. I would like to stream the file(s) content to the zip and stream the zip back to the client.
我正在使用 NodeJs (w/express) 并且我正在尝试将 zip 文件流回客户端。zip 中包含的文件不在文件系统中,而是动态创建的。我想将文件内容流式传输到 zip 并将 zip 流式传输回客户端。
I.E. I want the client to receive:
IE 我希望客户端收到:
tmp.zip
--> 1.txt
--> 2.txt
--> 3.txt
Where 1,2,3.txt are created on the fly and streamed to the zip file. Is this possible?
其中 1,2,3.txt 是动态创建的并流式传输到 zip 文件。这可能吗?
回答by CuddleBunny
Archiverhas an append method that lets you save text as a file. To "stream" that data to the user you can simply pipe to the HTTP response object.
Archiver有一个 append 方法,可以让您将文本保存为文件。要将数据“流式传输”给用户,您可以简单地通过管道传输到 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);
This will create a zip file with the two text files. The user visiting this page will be presented with a file download prompt.
这将创建一个包含两个文本文件的 zip 文件。访问此页面的用户将看到文件下载提示。
回答by slava
solution with: express.js, wait.for, zip-stream
解决方案: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();
});
回答by Dan Kohn
Yes, it's possible. I recommend taking a look at Streams Playgroundto get a feel for how Node Streams work.
是的,这是可能的。我建议查看Streams Playground以了解 Node Streams 的工作原理。
The zip compression in the core zlib library doesn't seem to support multiple files. If you want to go with tar-gzip, you could tar it with node-tar. But if you want ZIP, adm-ziplooks like the best option. Another possibility is node-archiver.
核心 zlib 库中的 zip 压缩似乎不支持多个文件。如果您想使用 tar-gzip,您可以使用node-tar对其进行tar。但是如果你想要 ZIP,adm-zip看起来是最好的选择。另一种可能性是node-archiver。
Update:
更新:
This exampleshows how to use Archiver, which supports streams. Just substitute fs.createReadStreamwith the streams you're creating dynamically, and have outputstream to Express's resrather than to fs.createWriteStream.
此示例展示了如何使用支持流的 Archiver。只需fs.createReadStream用您动态创建的流替换,并将流output传输到 Expressres而不是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');
});

