我正在使用 NodeJs(w/express),并且我正在尝试将 zip 文件流式传输回客户端。 zip 中包含的文件并不存在于文件系统中,而是动态创建的。我想将文件内容流式传输到 zip 并将 zip 流式传输回客户端。
即我希望客户收到:
tmp.zip
--> 1.txt
--> 2.txt
--> 3.txt
其中 1,2,3.txt 是动态创建的并流式传输到 zip 文件。这可能吗?
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 文件。访问此页面的用户将看到文件下载提示。
是的,这是可能的。我建议您查看 Streams Playground 以了解 Node Streams 的工作原理。
核心zlib库中的zip压缩似乎不支持多个文件。如果您想使用 tar-gzip,可以使用 node-tar 来 tar 。但如果您想要 ZIP,adm-zip 看起来是最好的选择。另一种可能性是node-archiver。
更新:
这个示例展示了如何使用支持流的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');
});
解决方案: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();
});
使用expressjs和node-zip将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);
}
}
使用存档器:
import archiver from 'archiver';
app.get('/get-zip-file', function (req, res) {
const files = ['1.txt', '2.txt', '3.txt'];
const archive = archiver('zip');
res.setHeader('content-type', 'application/zip');
archive.pipe(res);
for (const filename of files) {
archive.file(filename, { name: filename });
}
archive.finalize();
}