Какие функции для чтения файлов есть в модуле fs в Node.js?

«Какие функции для чтения файлов есть в модуле fs в Node.js?» — вопрос из категории Node.js Core, который задают на 26% собеседований Node.js Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Модуль fs в Node.js предоставляет несколько подходов к чтению файлов, каждый для своих сценариев.

1. Асинхронное чтение (неблокирующее, рекомендуется)

  • fs.readFile(path[, options], callback) — классический асинхронный метод с колбэком. Читает весь файл в память.
  • fsPromises.readFile(path[, options]) — современная Promise-версия того же метода. Я предпочитаю её из-за удобства использования с async/await.

2. Синхронное чтение (блокирующее)

  • fs.readFileSync(path[, options]) — синхронная версия. Блокирует цикл событий до завершения чтения. Использую её только в скриптах инициализации или CLI-утилитах, где простота важнее производительности.

3. Потоковое чтение (для больших файлов)

  • fs.createReadStream(path[, options]) — создает читаемый поток. Это ключевой метод для обработки файлов, которые не помещаются в память (например, логи, видео, большие дампы). Поток читает данные чанками, что экономит память.

Примеры использования:

const fs = require('fs');
const fsPromises = require('fs').promises;

// 1. Асинхронно с колбэком (устаревший, но встречается в легаси-коде)
fs.readFile('./config.json', 'utf8', (err, data) => {
  if (err) throw err;
  console.log('Конфиг загружен:', JSON.parse(data));
});

// 2. Асинхронно с Promises (современный стандарт)
async function loadConfig() {
  try {
    const data = await fsPromises.readFile('./config.json', 'utf8');
    return JSON.parse(data);
  } catch (err) {
    console.error('Ошибка чтения файла:', err);
  }
}

// 3. Синхронно (для инициализации)
const packageJson = fs.readFileSync('./package.json', 'utf8');
console.log('Версия приложения:', JSON.parse(packageJson).version);

// 4. Потоковое чтение большого файла
const readStream = fs.createReadStream('./huge-logfile.log', 'utf8');
let lineCount = 0;

readStream.on('data', (chunk) => {
  lineCount += (chunk.match(/n/g) || []).length;
});

readStream.on('end', () => {
  console.log(`Файл содержит примерно ${lineCount} строк.`);
});

Почему важен выбор? readFile прост для маленьких файлов, но createReadStream критически важен для масштабируемости и низкого потребления памяти при работе с большими данными.