2013-11-20 63 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我不認爲這是緩慢的,因爲模塊得到緩存。 –

相關問題