web-dev-qa-db-ja.com

Node.JSのcreateReadStream

だから私はfs.readFile()を使用しました

「致命的なエラー:CALL_AND_RETRY_LAST割り当てに失敗しました-プロセスがメモリ不足です」

fs.readFile()はコールバックを呼び出す前にファイル全体をメモリにロードするので、代わりにfs.createReadStream()を使用する必要がありますか?

それは私が以前readFileでやっていたことです:

fs.readFile('myfile.json', function (err1, data) {
    if (err1) {
        console.error(err1);
    } else {
        var myData = JSON.parse(data);
        //Do some operation on myData here
    }
}

申し訳ありませんが、ストリーミングは初めてです。以下は同じことをするがストリーミングで正しい方法ですか?

var readStream = fs.createReadStream('myfile.json');

readStream.on('end', function () {  
    readStream.close();
    var myData = JSON.parse(readStream);
    //Do some operation on myData here
});

ありがとう

10
user3421904

ファイルが非常に大きい場合は、はい、ストリーミングはそれを処理する方法です。ただし、2番目の例で実行しているのは、ストリームにすべてのファイルデータをメモリにバッファーさせ、endで処理することです。基本的にはreadFileと同じです。

JSONStream を確認してください。ストリーミングとは、データが流れるときにデータを処理することを意味します。あなたの場合、ファイル全体を一度にメモリにバッファリングすることができないので、明らかにこれを行う必要があります。それを念頭に置いて、うまくいけば、このようなコードは理にかなっています:

_JSONStream.parse('rows.*.doc')
_

一種のクエリパターンがあることに注意してください。これは、ファイルからのJSONオブジェクト/配列全体を一度に処理することができないため、JSONStreamが検出したデータをどのように処理するかについて、さらに検討する必要があるためです。それ

基本的に、JSONStreamを使用して、関心のあるJSONデータを照会できます。これにより、ファイル全体をメモリにバッファリングすることがなくなります。すべてのデータが必要な場合は、JSONStreamを使用してファイルを複数回ストリーミングし、その瞬間に必要なデータのみを取得する必要がありますが、あなたの場合はそうではないという欠点があります多くの選択。

また、JSONStreamを使用してデータを順番に解析し、データベースにダンプするようなこともできます。

_JSONStream.parse_は_JSON.parse_に似ていますが、オブジェクト全体を返すのではなく、ストリームを返します。解析ストリームがクエリに一致するオブジェクト全体を形成するのに十分なデータを取得すると、クエリに一致するドキュメントであるdataイベントを発行します。データハンドラーを構成したら、読み取りストリームを解析ストリームにパイプして、マジックの発生を監視できます。

例:

_var JSONStream = require('JSONStream');
var readStream = fs.createReadStream('myfile.json');
var parseStream = JSONStream.parse('rows.*.doc');
parseStream.on('data', function (doc) {
  db.insert(doc); // pseudo-code for inserting doc into a pretend database.
});
readStream.pipe(parseStream);
_

これは、何が起こっているのかを理解するのに役立つ詳細な方法です。これはより簡潔な方法です:

_var JSONStream = require('JSONStream');
fs.createReadStream('myfile.json')
  .pipe(JSONStream.parse('rows.*.doc'))
  .on('data', function (doc) {
    db.insert(doc);
  });
_

編集:

何が起こっているのかをさらに明確にするには、このように考えてみてください。巨大な湖があり、水を処理して浄化し、新しい貯水池に移動したいとします。巨大なバケツを備えた巨大な魔法のヘリコプターをお持ちの場合は、湖の上を飛んで、バケツに湖を置き、そこに処理用化学薬品を追加して、目的地まで飛行することができます。

もちろん問題は、そのような重量や体積に対応できるヘリコプターがないことです。それは単に不可能ですが、それは私たちが別の方法で目標を達成できないことを意味しません。したがって、代わりに、湖と新しい貯水池の間に一連の川(小川)を建設します。次に、これらの川に、通過する水を浄化するクレンジングステーションを設置します。これらのステーションは、さまざまな方法で運用できます。おそらく、処理は非常に速く行うことができるので、川を自由に流すことができ、水が最大速度で川を下るときに浄化が行われます。

また、水が処理されるまでに時間がかかる場合や、ステーションが効果的に処理する前に、一定量の水が必要な場合もあります。したがって、河川にゲートを設けるように設計し、湖から河川への水の流れを制御して、ステーションが作業を行い、浄化された水を下流から最終水まで放出するまで、必要な水だけをバッファーに流します先。

それは、まさにあなたがあなたのデータでやりたいことです。解析ストリームはクレンジングステーションであり、クエリに一致するドキュメント全体を形成するのに十分になるまでデータをバッファリングしてから、そのデータだけを下流にプッシュします(そしてdataイベントを発行します)。

ほとんどの場合、ゲートの開閉を処理する必要がないため、ノードストリームは素晴らしいです。 Nodeストリームは、ストリームが特定の量のデータをバッファリングするときに、逆流を制御するのに十分スマートです。まるでクレンジングステーションと湖のゲートが互いに通信して、完璧なフローを実現しているようです割合。

ストリーミングデータベースドライバーがある場合、理論的には、何らかの挿入ストリームを作成してから、dataイベントを手動で処理する代わりにparseStream.pipe(insertStream)を実行できます。これは、JSONファイルのフィルタリングされたバージョンを別のファイルに作成する例です。

_fs.createReadStream('myfile.json')
  .pipe(JSONStream.parse('rows.*.doc'))
  .pipe(JSONStream.stringify())
  .pipe(fs.createWriteStream('filtered-myfile.json'));
_
16
Chev