インターネットから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
メソッドを使用します。
コード:
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]));
}
});
});
アイデアは、バッファの配列を作成し、最後にそれらを連結して新しいものにすることです。これは、バッファーのサイズを変更できないためです。
更新
これは、オプションでencoding: null
を設定することにより、request
モジュールを使用してバッファー内の応答を取得するより単純なソリューションです。また、リダイレクトに従い、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
に直接渡すことができるバッファーであり、プロセス全体を簡素化します。
悲しいことに、ノードzlib
libで許可されているように、応答ストリームをunzipジョブにpipeすることはできません。キャッシュして、応答の終了を待ちます。大きなファイルの場合は、応答をfs
ストリームにパイプすることをお勧めします。そうしないと、瞬時にメモリがいっぱいになります。
私はあなたが何をしようとしているのか完全には理解していませんが、これは最良のアプローチです。 本当に必要な時間だけメモリにデータを保持する、そしてストリームを csv parser。
すべてのデータをメモリに保持したい場合は、csvパーサーメソッドfromPath
を、代わりにバッファを取得するfrom
に置き換え、getDataで直接unzipped
を返します。
node-Zip
の代わりに(@mihaiが言ったように)AMDZip
を使用できますが、AMDZip
はnpmでまだ公開されていないので注意が必要です。
$ npm install git://github.com/cthackers/adm-Zip.git
N.B。前提:Zipファイルに含まれるファイルは1つだけです
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
[〜#〜] edit [〜#〜]:入力Zipが大きすぎて読み込めない場合は機能しないことに注意してくださいstdinからのチャンク。大きなファイルを読み込む必要があり、Zipファイルに含まれるファイルが1つだけの場合は、unzip
の代わりに funzip を使用できます。
var unzip = spawn("funzip");
Zipファイルに複数のファイルが含まれている場合(そして、必要なファイルが最初のファイルではない場合)、運が悪いと言うことを恐れています。解凍するには、.Zip
ファイルはZipファイルは単なるコンテナであり、unzipはその中の最後のファイルを単にunzipする可能性があるためです。その場合、ファイルを一時的に保存する必要があります( node-temp が便利です)。
2日前のモジュールnode-Zip
がリリースされました。これは、ZipのJavaScript専用バージョンのラッパーです。 JSZip 。
var NodeZip = require('node-Zip')
, Zip = new NodeZip(zipBuffer.toString("base64"), { base64: true })
, unzipped = Zip.files["your-text-file.txt"].data;