Node.jsでファイルシステムを操作する実践ガイド

ファイルシステムの基礎

Node.jsは標準でファイルやディレクトリを扱うためのAPIを提供しており、fsモジュールがその中心となる。非同期処理を前提としたプロミス版APIを使えば、スレッドをブロックすることなく効率的にI/Oを実行できる。

// プロミス版をインポート
import { promises as fsp } from 'fs';

ディレクトリの一覧取得

readdirwithFileTypes: trueを渡すと、ファイル名の文字列ではなくDirentオブジェクトの配列が得られる。これにより、isFile()isDirectory()などの判別メソッドが使える。

const entries = await fsp.readdir('./data', { withFileTypes: true });
for (const entry of entries) {
  console.log(entry.name, entry.isDirectory() ? 'dir' : 'file');
}

再帰的なディレクトリ走査

ディレクトリを深く掘り下げてファイルを列挙するには、非同期再帰を利用する。Promiseを返すようにすることで、呼び出し元でawaitできる。

async function collectFiles(root) {
  const results = [];
  const stack = [root];

  while (stack.length) {
    const current = stack.pop();
    const list = await fsp.readdir(current, { withFileTypes: true });

    for (const node of list) {
      const fullPath = `${current}/${node.name}`;
      if (node.isDirectory()) {
        stack.push(fullPath);
      } else {
        results.push(fullPath);
      }
    }
  }
  return results;
}

ファイル・ディレクトリの生成

ディレクトリを作成する

mkdirは親ディレクトリが存在しないと失敗するため、{ recursive: true }を付与すると階層ごと自動生成できる。

await fsp.mkdir('./logs/2024/06', { recursive: true });

新規ファイルへの書き込み

writeFileは同名ファイルが存在すると上書きする。追記したい場合はフラグを指定する。

await fsp.writeFile('./logs/app.log', 'start\n');
await fsp.writeFile('./logs/app.log', 'append\n', { flag: 'a' });

データの読み書きと加工

バッファから文字列へ

readFileはデフォルトでBufferを返す。utf8オプションを付けると文字列に変換される。

const raw = await fsp.readFile('./config.json', 'utf8');
const config = JSON.parse(raw);

パス操作のベストプラクティス

OSごとの区切り文字の違いを吸収するにはpathモジュールを活用する。

import path from 'path';

const target = path.join(__dirname, 'assets', 'images', 'logo.png');
const parsed  = path.parse(target);

console.log(parsed.dir);   // /home/user/project/assets/images
console.log(parsed.name);  // logo
console.log(parsed.ext);   // .png

実装例:設定ファイルの自動生成

以下は、指定ディレクトリ内の全画像パスをJSONにまとめて保存するスクリプトである。

import { promises as fsp } from 'fs';
import path from 'path';

async function buildImageIndex(dir) {
  const files = await collectFiles(dir);
  const images = files
    .filter(f => ['.png', '.jpg', '.jpeg'].includes(path.extname(f)))
    .map(f => path.relative(process.cwd(), f));

  await fsp.writeFile(
    path.join(dir, 'index.json'),
    JSON.stringify(images, null, 2)
  );
}

await buildImageIndex('./assets');

タグ: Node.js fs path async/await JSON

6月10日 16:13 投稿