Node.jsで大量のCSVデータやログファイルを扱う際、ファイルを一括で読み込んで処理すると、メモリ使用量が急増し、パフォーマンスに深刻な影響を与えることがあります。
そのようなケースでは、Node.jsのストリーム機能を使って、ファイルを一行ずつ処理する「逐次処理」を行うことで、高速かつ省メモリな処理が可能になります。
本記事では、CSVファイルやログファイルを対象に、Node.jsのstream
モジュールを使って効率よく読み書きする方法を具体的に解説します。
ストリームとは?|全体読み込みとの違い
通常、Node.jsではfs.readFile()
を使ってファイル全体をメモリに読み込む方法がよく使われますが、100MBを超えるファイルなどでは非効率です。
一方、ストリームを使えば、ファイルをチャンク(小さなデータ片)
に分割して処理するため、メモリ消費を抑えながら処理できます。
基本:読み取りストリーム(ReadStream)の使い方
fs.createReadStream()
を使用して、CSVやログファイルを一行ずつ処理する方法を見てみましょう。
const fs = require('fs');
const readline = require('readline');
const rl = readline.createInterface({
input: fs.createReadStream('data.csv'),
crlfDelay: Infinity
});
rl.on('line', (line) => {
console.log('1行読み込み:', line);
});
rl.on('close', () => {
console.log('すべての行を処理しました。');
});
readline
モジュールと組み合わせることで、CSVやログを一行単位で処理できます。
CSVのパースにはcsv-parserが便利
CSVファイルをオブジェクト形式で扱いたい場合は、csv-parser
モジュールが便利です。
npm install csv-parser
const fs = require('fs');
const csv = require('csv-parser');
fs.createReadStream('data.csv')
.pipe(csv())
.on('data', (row) => {
console.log('読み込んだデータ:', row);
})
.on('end', () => {
console.log('CSVファイルの読み込みが完了しました');
});
この方法なら、ヘッダー付きのCSVファイルをJSONオブジェクトとして1行ずつ処理可能です。
書き込みストリーム(WriteStream)の使い方
処理結果を新しいファイルに書き出すには、fs.createWriteStream()
を使います。
const writeStream = fs.createWriteStream('output.txt');
writeStream.write('処理結果をここに書き込みます\n');
writeStream.end('最終行です');
CSVを読み込みながら特定の条件に応じて出力ファイルに書き出す処理もストリームで連携できます。
ストリーム処理の組み合わせ例
読み込み→整形→書き出しの一連の処理は、ストリームをpipe
でつなげることで効率化できます:
const csv = require('csv-parser');
const fs = require('fs');
const readStream = fs.createReadStream('input.csv');
const writeStream = fs.createWriteStream('filtered.csv');
readStream
.pipe(csv())
.on('data', (row) => {
if (row.status === 'active') {
writeStream.write(`${row.id},${row.name},${row.status}\n`);
}
})
.on('end', () => {
writeStream.end();
console.log('アクティブな行のみを書き出しました');
});
パフォーマンスを意識した実装のポイント
- バッファサイズの調整:
highWaterMark
でメモリ効率を制御可能 - エラーハンドリング:
.on('error')
で例外処理を必ず設定 - バックプレッシャー制御:
pipe
を使うことで内部的に制御される
まとめ
Node.jsにおいて、大量のCSVファイルやログファイルを扱う際は、ストリームによる逐次処理が極めて有効です。
fs.createReadStream()
やcsv-parser
、createWriteStream()
を活用することで、パフォーマンスを維持しつつ柔軟なデータ処理が可能になります。
今後は、非同期処理と組み合わせてさらに高度なパイプラインを構築することで、ビッグデータやログ集計処理への応用も期待できます。