Panda Noir

JavaScript の限界を究めるブログでした。最近は幅広めに書いてます。

Node.jsのFile操作まとめ

ファイル読み込み処理を書くたびにググっているので、まとめてみました。書き込みについては、読み込みとほぼ同じなので割愛します。

async/await + util.promisify (最適解っぽい)

async/awaitが使える環境なら(おそらく)これが最強です

// 必要なものを読み込む
const fs = require('fs'), util = require('util');
const readFile = util.promisify(fs.readFile).bind(util);

(async () => {
    const path = './hoge.txt';
    const data = await readFile(path, 'utf8'); // これでhoge.txtの内容が読み出せる!
})();
メリット
短く可読性が高い
例外処理も行うことができる
処理をブロックしない

async/awaitを使うパターン

const fs = require('fs');

const readFile = path => new Promise((resolve, reject) => {
    fs.readFile(path, 'utf8', (err, data) => {
        if (err) { reject(err); return; }
        resolve(data);
    });
});

// エラーを握りつぶしてもいいならreadFile()は次のようにできます
// const readFile = path => new Promise(r =>
//     fs.readFile(path, 'utf8', (_, data) => r(data)));

(async () => {
    const path = './hoge.txt';
    const data = await readFile(path);
})();
メリット
処理をブロックしない
かなり読みやすい
例外処理もできる

async/awaitは使えるなら積極的に使ったほうが良いと思います。

async/await なしパターン

const fs = require('fs');
const path = './hoge.txt';

fs.readFile(path, 'utf8', (err, data) => {
    if (err) throw err;
    // something...
})
メリット
処理をブロックしない

ただし、処理部分(something...)が中に埋まっていて、可読性が落ちています。

同期的に操作するパターン

const fs = require('fs');
const path = './hoge.txt';

const data = fs.readFileSync(path, 'utf8');
メリットデメリット
シンプルに書けるパフォーマンスが低下する
可読性は高い

このパターンはパフォーマンスがとにかく落ちやすいので、書き捨てる時以外はなるべく使用しないほうがいいです。

readStreamを使うパターン

const fs = require('fs');
const path = './hoge.txt';

const rs = fs.createReadStream(path, {highWaterMark: 10}); //bufferSizeではなくhighWaterMarkになった
rs.setEncoding('utf8');

rs.on('data', data => console.log(data));

これは上3つとは異なるユースケースなので、メリット・デメリットを語ってもあまり意味がないですね。

このパターンは、大きいデータを読み込んで順次処理するのに向いています。

fs.openを使うパターン

これはめったに使わないと思います・・・いつ使うのでしょうか。

const fs = require('fs');
const path = './hoge.txt';

fs.open(path, 'r', (err, fd) => {
    fs.fstat(fd, (err, stats) => {
        const bufferSize = stats.size,
            buffer = new Buffer(bufferSize); // ここにデータが蓄積されていく
        let chunkSize = 5, // チャンクサイズ
            bytesRead = 0; // これまでに読んできたバイト数
        while (bytesRead < bufferSize) {
            if (bytesRead + chunkSize > bufferSize) {
                chunkSize = bufferSize - bytesRead;
            }
            fs.read(fd, buffer, bytesRead, chunkSize, bytesRead, (err, bytesRead, buffer) => {});
            bytesRead += chunkSize; // いままで読み込んだバイト数を更新
        }
        console.log(buffer.toString('utf8')); // 読み終わった
        fs.close(fd, err => console.log(err));
    });
});