[英]How to download and unzip a zip file in memory in NodeJs?
我想從 Internet 下載一個 zip 文件並將其解壓縮到內存中而不保存到臨時文件。 我怎樣才能做到這一點?
這是我嘗試過的:
var url = 'http://bdn-ak.bloomberg.com/precanned/Comdty_Calendar_Spread_Option_20120428.txt.zip';
var request = require('request'), fs = require('fs'), zlib = require('zlib');
request.get(url, function(err, res, file) {
if(err) throw err;
zlib.unzip(file, function(err, txt) {
if(err) throw err;
console.log(txt.toString()); //outputs nothing
});
});
[編輯] 根據建議,我嘗試使用 adm-zip 庫,但仍然無法完成這項工作:
var ZipEntry = require('adm-zip/zipEntry');
request.get(url, function(err, res, zipFile) {
if(err) throw err;
var zip = new ZipEntry();
zip.setCompressedData(new Buffer(zipFile.toString('utf-8')));
var text = zip.getData();
console.log(text.toString()); // fails
});
您需要一個可以處理緩沖區的庫。 最新版本的adm-zip
將執行以下操作:
npm install adm-zip
我的解決方案使用http.get
方法,因為它返回 Buffer 塊。
代碼:
var file_url = 'http://notepad-plus-plus.org/repository/7.x/7.6/npp.7.6.bin.x64.zip';
var AdmZip = require('adm-zip');
var http = require('http');
http.get(file_url, function(res) {
var data = [], dataLen = 0;
res.on('data', function(chunk) {
data.push(chunk);
dataLen += chunk.length;
}).on('end', function() {
var buf = Buffer.alloc(dataLen);
for (var i = 0, len = data.length, pos = 0; i < len; i++) {
data[i].copy(buf, pos);
pos += data[i].length;
}
var zip = new AdmZip(buf);
var zipEntries = zip.getEntries();
console.log(zipEntries.length)
for (var i = 0; i < zipEntries.length; i++) {
if (zipEntries[i].entryName.match(/readme/))
console.log(zip.readAsText(zipEntries[i]));
}
});
});
這個想法是創建一個緩沖區數組,並在最后將它們連接成一個新的緩沖區。 這是因為緩沖區不能調整大小。
更新
這是一個更簡單的解決方案,它使用request
模塊通過在選項中設置encoding: null
來獲取緩沖區中的響應。 它還遵循重定向並自動解析 http/https。
var file_url = 'https://github.com/mihaifm/linq/releases/download/3.1.1/linq.js-3.1.1.zip';
var AdmZip = require('adm-zip');
var request = require('request');
request.get({url: file_url, encoding: null}, (err, res, body) => {
var zip = new AdmZip(body);
var zipEntries = zip.getEntries();
console.log(zipEntries.length);
zipEntries.forEach((entry) => {
if (entry.entryName.match(/readme/i))
console.log(zip.readAsText(entry));
});
});
響應的body
是一個可以直接傳遞給AdmZip
,從而簡化了整個過程。
遺憾的是,您無法將響應流通過管道傳輸到解壓縮作業中,因為 node zlib
lib 允許您這樣做,您必須緩存並等待響應結束。 我建議你在大文件的情況下將響應通過管道傳輸到fs
流,否則你會在眨眼間填滿你的記憶!
我不完全理解你想要做什么,但恕我直言,這是最好的方法。 您應該只在真正需要時才將數據保存在內存中,然后傳輸到csv 解析器。
如果您想將所有數據保存在內存中,您可以將 csv 解析器方法fromPath
替換為from
,它需要一個緩沖區,並在 getData 中直接unzipped
返回
您可以使用AMDZip
(如@mihai 所說)而不是node-zip
,請注意,因為AMDZip
尚未在 npm 中發布,因此您需要:
$ npm install git://github.com/cthackers/adm-zip.git
NB 假設:zip 文件只包含一個文件
var request = require('request'),
fs = require('fs'),
csv = require('csv')
NodeZip = require('node-zip')
function getData(tmpFolder, url, callback) {
var tempZipFilePath = tmpFolder + new Date().getTime() + Math.random()
var tempZipFileStream = fs.createWriteStream(tempZipFilePath)
request.get({
url: url,
encoding: null
}).on('end', function() {
fs.readFile(tempZipFilePath, 'base64', function (err, zipContent) {
var zip = new NodeZip(zipContent, { base64: true })
Object.keys(zip.files).forEach(function (filename) {
var tempFilePath = tmpFolder + new Date().getTime() + Math.random()
var unzipped = zip.files[filename].data
fs.writeFile(tempFilePath, unzipped, function (err) {
callback(err, tempFilePath)
})
})
})
}).pipe(tempZipFileStream)
}
getData('/tmp/', 'http://bdn-ak.bloomberg.com/precanned/Comdty_Calendar_Spread_Option_20120428.txt.zip', function (err, path) {
if (err) {
return console.error('error: %s' + err.message)
}
var metadata = []
csv().fromPath(path, {
delimiter: '|',
columns: true
}).transform(function (data){
// do things with your data
if (data.NAME[0] === '#') {
metadata.push(data.NAME)
} else {
return data
}
}).on('data', function (data, index) {
console.log('#%d %s', index, JSON.stringify(data, null, ' '))
}).on('end',function (count) {
console.log('Metadata: %s', JSON.stringify(metadata, null, ' '))
console.log('Number of lines: %d', count)
}).on('error', function (error) {
console.error('csv parsing error: %s', error.message)
})
})
如果您使用的是 MacOS 或 Linux,則可以使用unzip
命令從stdin
解壓縮。
在這個例子中,我將文件系統中的 zip 文件讀入一個Buffer
對象,但它也適用於下載的文件:
// Get a Buffer with the zip content
var fs = require("fs")
, zip = fs.readFileSync(__dirname + "/test.zip");
// Now the actual unzipping:
var spawn = require('child_process').spawn
, fileToExtract = "test.js"
// -p tells unzip to extract to stdout
, unzip = spawn("unzip", ["-p", "/dev/stdin", fileToExtract ])
;
// Write the Buffer to stdin
unzip.stdin.write(zip);
// Handle errors
unzip.stderr.on('data', function (data) {
console.log("There has been an error: ", data.toString("utf-8"));
});
// Handle the unzipped stdout
unzip.stdout.on('data', function (data) {
console.log("Unzipped file: ", data.toString("utf-8"));
});
unzip.stdin.end();
這實際上只是以下的節點版本:
cat test.zip | unzip -p /dev/stdin test.js
編輯:值得注意的是,如果輸入 zip 太大而無法從 stdin 中讀取一個塊,這將不起作用。 如果您需要讀取更大的文件,而您的 zip 文件只包含一個文件,您可以使用funzip而不是unzip
:
var unzip = spawn("funzip");
如果您的 zip 文件包含多個文件(並且您想要的文件不是第一個),我恐怕會說您不走運。 Unzip 需要在.zip
文件中查找,因為 zip 文件只是一個容器,而 unzip 可能只是解壓縮其中的最后一個文件。 在這種情況下,您必須臨時保存文件( node-temp派上用場)。
var fs = require('fs); var unzip = require('unzip');
//將a.zip解壓縮到當前字典
fs.createReadStream('。/ path / a.zip')。pipe(unzip.Extract({path:'。/ path /'}));
我使用解壓縮模塊,它工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.