[Node.js] ディレクトリをコピーする方法

3 min read

概要

Node.jsのfsモジュールにはファイルのコピーをするためのcopyFileが存在する。しかし、ディレクトリをコピーするためのcopyDirectoryのようなメソッドは存在しないため、必要があれば独自で作る必要がある。

fs-extraなどのようなライブラリには、この手の関数が存在しているため利用されることが多い。

コード

以下のコードでディレクトリをコピーできる。ただ、条件として素のNode.jsではなく、TypeScriptで記述しているため、node-tsesbuildなどのコンパイラを用いて実行する必要がある。

import { copyFile, mkdir, readdir } from 'node:fs/promises';
import path from 'node:path';

export const copyDir = async (src: string, dest: string) => {
  try {
    // コピー先ディレクトリが存在しない場合、作成する
    await mkdir(dest, { recursive: true });

    // コピー元のファイル・ディレクトリ一覧を取得する
    const files = await readdir(src, { withFileTypes: true });

    // 各エントリに対してコピー操作を実行する
    for (const file of files) {
      const srcPath = path.join(src, file.name);
      const destPath = path.join(dest, file.name);

      if (file.isDirectory()) {
        // ディレクトリの場合、再帰的にコピーする
        await copyDir(srcPath, destPath);
      } else {
        // ファイルの場合、単純にコピーする
        await copyFile(srcPath, destPath);
      }
    }
  } catch (error) {
    console.error(`Error while copying directory: ${error}`);
  }
};

createReadStream(src)createWriteStream(dst)を利用してメモリ効率の良いコピーもできるが、今回は簡易的にcopyFileを利用する。

解説

ディレクトリは単なるファイルではなく、ファイルを参照するためのデータ構造である。そのため、ディレクトリをコピーするためには、ディレクトリを作成し中のファイルを再帰的にコピーする必要がある。

  1. コピー先ディレクトリの生成
    最初に、コピー先のディレクトリを生成する。

    await mkdir(dest, { recursive: true });
    
  2. コピー元の一覧取得
    readdirメソッドを用いて、コピー元のファイルおよびディレクトリの一覧を取得する。

    const files = await readdir(src, { withFileTypes: true });
    
  3. ファイルおよびディレクトリのコピー
    コピー元の一覧に基づき、ファイルとディレクトリをコピーする。ディレクトリの場合は、同じことを繰り返すため再帰的に処理を行う。

    if (file.isDirectory()) {
      await copyDir(srcPath, destPath);
    } else {
      await copyFile(srcPath, destPath);
    }