2013-11-20 60 views
18

我正在使用NodeJs(w/express),我试图将zip文件流式传输回客户端。包含在zip中的文件不在文件系统中,而是动态创建。我想将文件内容传输到压缩文件并将压缩文件传输回客户端。动态创建并流式传输到客户端

I.E.我想在客户端接收:

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

凡1,2,3.txt是动态创建和流式传输到zip文件。这可能吗?

+0

目前尚不清楚你的意思是什么“传输到压缩文件” ...没有HTTP客户端收到什么文件到底?压缩文件?或三个文本文件的连接? –

+0

对不起,格式化搞砸了,希望更清楚。我想发回一个zip文件。 2个主要问题我正在试着围住我的头。 1.如何将zip压缩回来,2.如何将我的3个文件流式传输到我正在回传的zip文件。 I.E.我已经看到了一些流式处理文件系统中已存在的文件的例子,但是如果你是在飞行中创建文件,没有例子说明如何做到这一点。 – lostintranslation

+0

您是否能够找到相同的解决方法? –

回答

31

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文件。访问此页面的用户将看到文件下载提示。与

+0

现在追加所有文件后创建的zip文件会发生什么变化。我有类似的情况,我想让我的用户一次下载多个文件。因为我无法将它们与响应一起发送。我必须创建一个临时zip文件并将文件移入其中,然后将其提供,然后将其删除以便存储空间。那么,archiver是否会创建一个临时的zip文件或永久的zip文件? – DeadMan

+1

我不认为这个zip文件是“创建的”,因为该实用程序正在构建zip文件,因此它正在将这些位写入流,这将是对http请求的响应。我确定它存在于某处的RAM中(或者如果它相当大,则交换文件)一段时间,直到文件被发送到浏览器,然后该浏览器将其保存到最终用户的临时文件,直到它被保存到一个位置。这段代码现在已经运行了几个月,我的磁盘空间使用率保持不变,因此没有任何临时的Zip需要清除。 – CuddleBunny

+1

Archiver repo的这个例子帮助了我。 https://github.com/ctalkington/node-archiver/blob/master/examples/express.js – eephillip

3

是的,这是可能的。我建议看看Streams Playground以了解节点流如何工作。

核心zlib库中的zip压缩似乎不支持多个文件。如果你想使用tar-gzip,你可以使用node-tar来解决问题。但如果你想要ZIP,adm-zip看起来是最好的选择。另一种可能性是node-archiver

更新:

example展示了如何使用归档,支持流。只需将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'); 
}); 
+0

混淆部分是我不知道如何使用adm-zip(或任何其他zip模块)将zip写入流而不是文件系统上的文件。更不用说将文件流式传输到压缩文件,然后流式传输到客户端。 – lostintranslation

+0

我为你添加了一个例子。 – dankohn

+0

看过这个例子。仍然无法得到我需要的工作。我正在创建file1和file2(它们不在文件系统上)。当我为这些文件创建数据时,我想将它添加到流中。但是我想将它添加到压缩文件中。 – lostintranslation

5

解决方案:express.js,wait.for,拉链流

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

}); 
+0

@ZachB我不认为这是缓慢的,因为模块得到缓存。 –

相关问题