Back

Node.js ファイル書き込みの解説:fs.writeSyncについて知っておくべきすべてのこと

Node.js ファイル書き込みの解説:fs.writeSyncについて知っておくべきすべてのこと

ファイル操作はNode.jsアプリケーションの基本的な部分です。設定ファイルの作成、データのログ記録、レポートの生成など、ファイルを効率的に書き込む方法を理解することは不可欠です。fs.writeFileSync()メソッドは、Node.jsで同期的にファイル書き込み操作を処理するための簡単な方法を提供します。

この包括的なガイドでは、基本的な使用法から高度なテクニックとベストプラクティスまで、fs.writeFileSync()を効果的に使用するために知っておくべきすべてについて説明します。

重要なポイント

  • fs.writeFileSync()は同期的にファイルにデータを書き込み、完了するまでイベントループをブロックします
  • 常にtry-catchブロックを使用して潜在的なエラーを処理しましょう
  • ファイルの作成と書き込み動作を制御するために適切なフラグを使用しましょう
  • 高並行性アプリケーションではパフォーマンスへの影響を考慮しましょう
  • 大きなファイルや本番環境では、非同期の代替手段を検討しましょう
  • 特にユーザー提供のパスを扱う場合は、セキュリティ上の懸念に注意しましょう

fs.writeFileSync()とは?

fs.writeFileSync()は、Node.jsのファイルシステム(fs)モジュールに組み込まれたメソッドで、同期的にファイルにデータを書き込みます。非同期版とは異なり、このメソッドはファイル操作が完了するまでコードの実行をブロックします。

メソッドのシグネチャは次のとおりです:

fs.writeFileSync(file, data[, options])
  • file: ファイルへのパス(文字列、Buffer、URL、またはファイル記述子)
  • data: 書き込む内容(文字列、Buffer、TypedArray、またはDataView)
  • options: オプションの設定パラメータ(文字列またはオブジェクト)

fs.writeFileSync()の基本的な使用法

シンプルなテキストファイルの書き込み

const fs = require('fs');

try {
  fs.writeFileSync('example.txt', 'Hello, Node.js!');
  console.log('File written successfully');
} catch (err) {
  console.error('Error writing file:', err);
}

このコードは、現在の作業ディレクトリに「Hello, Node.js!」という内容のexample.txtというファイルを作成します。

JSONデータの書き込み

const fs = require('fs');

const user = {
  name: 'John Doe',
  age: 30,
  email: 'john@example.com'
};

try {
  fs.writeFileSync('user.json', JSON.stringify(user, null, 2));
  console.log('JSON file written successfully');
} catch (err) {
  console.error('Error writing JSON file:', err);
}

パラメータの理解

ファイルパラメータ

最初のパラメータは、データが書き込まれるファイルパスを指定します:

// 現在のディレクトリに書き込む
fs.writeFileSync('data.txt', 'Some content');

// 特定のパスに書き込む
fs.writeFileSync('/var/logs/app.log', 'Log entry');

// ファイル記述子を使用する
const fd = fs.openSync('config.json', 'w');
fs.writeFileSync(fd, '{""setting"": ""value""}');
fs.closeSync(fd);

重要:指定されたディレクトリが存在しない場合、Node.jsはエラーをスローします。fs.writeFileSync()メソッドはディレクトリを作成できません。

データパラメータ

2番目のパラメータには、書き込みたい内容が含まれます:

// 文字列の書き込み
fs.writeFileSync('file.txt', 'Plain text content');

// Bufferの書き込み
const buffer = Buffer.from('Binary content');
fs.writeFileSync('binary.dat', buffer);

// TypedArrayの書き込み
const uint8Array = new Uint8Array([72, 101, 108, 108, 111]);  // ASCIIで""Hello""
fs.writeFileSync('typed-array.txt', uint8Array);

Bufferの操作

バッファはバイナリデータを扱う際に特に便利です:

const fs = require('fs');

// 文字列からバッファを作成
const stringData = 'Hello World';
const buffer = Buffer.from(stringData, 'utf8');
fs.writeFileSync('buffer-example.txt', buffer);

// JSONからバッファを作成
const userData = { name: 'Alice', age: 28 };
const jsonBuffer = Buffer.from(JSON.stringify(userData));
fs.writeFileSync('user-data.json', jsonBuffer);

オプションパラメータ

3番目のパラメータでは、ファイル書き込みの動作をカスタマイズできます:

fs.writeFileSync('config.txt', 'Configuration data', {
  encoding: 'utf8',    // 文字エンコーディング(デフォルト:'utf8')
  mode: 0o666,         // ファイルのパーミッション(デフォルト:0o666)
  flag: 'w'            // ファイルシステムフラグ(デフォルト:'w')
});

一般的なフラグオプション

  • 'w': 書き込み用に開く、存在しない場合は作成、存在する場合は切り詰める(デフォルト)
  • 'a': 追加用に開く、存在しない場合は作成
  • 'wx': ‘w’と同様だが、パスが存在する場合は失敗
  • 'ax': ‘a’と同様だが、パスが存在する場合は失敗
  • 'r+': 読み書き用に開く、ファイルは存在する必要がある
// ファイルを上書きする代わりに追加する
fs.writeFileSync('log.txt', 'New log entryn', { flag: 'a' });

// ファイルが存在しない場合にのみ新しいファイルを作成する
try {
  fs.writeFileSync('config.json', '{}', { flag: 'wx' });
  console.log('New config file created');
} catch (err) {
  if (err.code === 'EEXIST') {
    console.log('Config file already exists');
  } else {
    console.error('Error:', err);
  }
}

fs.writeFileSync()でのエラー処理

fs.writeFileSync()は同期的なので、エラーは直接スローされます。常にtry-catchブロックで囲むようにしましょう:

try {
  fs.writeFileSync('/path/to/file.txt', 'Content');
} catch (error) {
  // 特定のエラータイプを処理する
  if (error.code === 'ENOENT') {
    console.error('Directory does not exist');
  } else if (error.code === 'EACCES') {
    console.error('Permission denied');
  } else {
    console.error('Unexpected error:', error);
  }
}

一般的なエラーコード

  • ENOENT: そのようなファイルまたはディレクトリがない(多くの場合、親ディレクトリが存在しない場合)
  • EACCES: アクセス拒否
  • EISDIR: ディレクトリである(ディレクトリに書き込もうとしている)
  • EMFILE: 開いているファイルが多すぎる
  • EEXIST: ファイルがすでに存在する(‘wx’または’ax’フラグを使用する場合)

実用的なユースケース

設定ファイルの作成

const fs = require('fs');

function createDefaultConfig() {
  const config = {
    apiKey: '',
    debug: false,
    logLevel: 'info',
    maxRetries: 3
  };
  
  try {
    fs.writeFileSync('config.json', JSON.stringify(config, null, 2));
    console.log('Default configuration created');
  } catch (err) {
    console.error('Failed to create config file:', err);
  }
}

// 設定が存在するかチェックし、存在しない場合は作成
try {
  fs.accessSync('config.json', fs.constants.F_OK);
  console.log('Config file already exists');
} catch (err) {
  createDefaultConfig();
}

シンプルなログ記録

function logMessage(message) {
  const timestamp = new Date().toISOString();
  const logEntry = `[${timestamp}] ${message}n`;
  
  try {
    fs.writeFileSync('app.log', logEntry, { flag: 'a' });
  } catch (err) {
    console.error('Failed to write to log file:', err);
  }
}

logMessage('Application started');
// 何か処理を行う
logMessage('Operation completed');

ユーザー入力の保存

const readline = require('readline-sync');
const fs = require('fs');

function saveUserData() {
  const username = readline.question('Enter username: ');
  
  if (!username) {
    console.log('Username cannot be empty');
    return saveUserData();
  }
  
  try {
    const userData = {
      username,
      createdAt: new Date().toISOString()
    };
    
    fs.writeFileSync(`${username}.json`, JSON.stringify(userData, null, 2));
    console.log(`User data saved to ${username}.json`);
  } catch (err) {
    console.error('Failed to save user data:', err);
  }
}

saveUserData();

パフォーマンスに関する考慮事項

同期処理と非同期処理

fs.writeFileSync()は操作が完了するまでイベントループをブロックするため、高並行性アプリケーションではパフォーマンスに影響を与える可能性があります:

// 同期(イベントループをブロック)
console.time('writeFileSync');
fs.writeFileSync('large-file.txt', 'X'.repeat(1000000));
console.timeEnd('writeFileSync');

// 非同期(イベントループをブロックしない)
console.time('writeFile');
fs.writeFile('large-file-async.txt', 'X'.repeat(1000000), () => {
  console.timeEnd('writeFile');
});
console.log('This runs immediately while file is being written');

fs.writeFileSync()を使用するタイミング

fs.writeFileSync()を使用するのは以下の場合です:

  • 小さなファイルを書き込む場合
  • スクリプトやCLIツールで作業している場合
  • 実行を続ける前にファイルを書き込む必要がある場合
  • 起動/初期化フェーズにある場合

fs.writeFileSync()を避けるべき場合:

  • 大きなファイルを扱う場合
  • 高並行性のWebサーバーで
  • パフォーマンスが重要なコードパスで
  • 複数のファイル操作を同時に処理する場合

モダンな代替手段

ESモジュール構文の使用

// ESM構文(Node.js 12以上が必要)
import { writeFileSync } from 'fs';
import { join } from 'path';

const filePath = join(process.cwd(), 'data.txt');
writeFileSync(filePath, 'Content using ESM syntax');

fs/promises APIの使用

同期的な動作を維持しながらよりモダンなプロミスアプローチを使用する場合:

// トップレベルのawaitを使用したfs/promises(Node.js 14.8以上、ESM使用)
import { writeFile } from 'fs/promises';

try {
  // これは依然として非同期ですが、より洗練された構文です
  await writeFile('example.txt', 'Content with promises');
  console.log('File written successfully');
} catch (err) {
  console.error('Error writing file:', err);
}

セキュリティに関する考慮事項

ファイルのパーミッション

機密データを書き込む際にはファイルのパーミッションに注意しましょう:

// 機密ファイルに制限的なパーミッションを設定
fs.writeFileSync('credentials.json', JSON.stringify(credentials), {
  mode: 0o600  // 所有者のみ読み書き可能
});

パストラバーサルの脆弱性

特にユーザー入力から来る場合は、常にファイルパスを検証およびサニタイズしましょう:

const path = require('path');
const fs = require('fs');

function safeWriteFile(filename, content) {
  // パストラバーサルを防ぐためにファイル名をサニタイズ
  const safeName = path.basename(filename);
  const safePath = path.join('./uploads', safeName);
  
  try {
    fs.writeFileSync(safePath, content);
    return true;
  } catch (err) {
    console.error('Error writing file:', err);
    return false;
  }
}

一般的な問題のトラブルシューティング

""ENOENT: no such file or directory""

これは通常、ディレクトリが存在しないことを意味します:

const fs = require('fs');
const path = require('path');

function writeFileWithDirectoryCreation(filePath, content) {
  const directory = path.dirname(filePath);
  
  try {
    // ディレクトリが存在しない場合は作成
    if (!fs.existsSync(directory)) {
      fs.mkdirSync(directory, { recursive: true });
    }
    
    // ファイルを書き込む
    fs.writeFileSync(filePath, content);
    return true;
  } catch (err) {
    console.error('Error:', err);
    return false;
  }
}

writeFileWithDirectoryCreation('logs/app/data.log', 'Log entry');

""EACCES: permission denied""

ファイルとディレクトリのパーミッションを確認してください:

try {
  fs.writeFileSync('/var/log/app.log', 'Log entry');
} catch (err) {
  if (err.code === 'EACCES') {
    // 別の場所に書き込みを試みる
    const homeDir = require('os').homedir();
    fs.writeFileSync(path.join(homeDir, 'app.log'), 'Log entry');
    console.log('Wrote to home directory instead');
  }
}

結論

fs.writeFileSync()メソッドは、Node.jsアプリケーションでファイル操作を処理するための強力なツールです。同期的にファイルにデータを書き込むための簡単な方法を提供し、実行を続ける前に確実に完了する必要があるシナリオに最適です。そのパラメータ、エラー処理、パフォーマンスへの影響を理解することで、一般的な落とし穴を避けながらプロジェクトでこのメソッドを効果的に使用できます。

このメソッドの同期的な性質を考慮し、アプリケーションのニーズに基づいて適切に使用することを忘れないでください。高並行性アプリケーションや大きなファイルを扱う場合は、fs.writeFile()やストリームなどの非同期の代替手段を検討してください。

よくある質問

fs.writeFileSync()は、スクリプト、CLIツール、または初期化コードなど、続行する前に確実に完了する必要がある場合に使用します。fs.writeFile()は、特にサーバーやパフォーマンスが重要なアプリケーションなど、他のほとんどのシナリオで使用します。

オプションパラメータで'a'フラグを使用します:fs.writeFileSync('log.txt', '新しいエントリn', { flag: 'a' });

いいえ、ファイルのみ作成できます。ディレクトリを最初に作成するにはfs.mkdirSync()を使用する必要があります。

Bufferオブジェクトを使用します:const buffer = Buffer.from([0x48, 0x65, 0x6c, 0x6c, 0x6f]); // 'Hello' fs.writeFileSync('binary.dat', buffer);

APIに特定の制限はありませんが、非常に大きなファイルを同期的に書き込むとイベントループが長時間ブロックされる可能性があります。数MBを超えるファイルの場合は、ストリームや非同期メソッドの使用を検討してください。

modeオプションを設定します:fs.writeFileSync('sensitive.txt', 'private data', { mode: 0o600 });

Listen to your bugs 🧘, with OpenReplay

See how users use your app and resolve issues fast.
Loved by thousands of developers