JavaRush /Курсы /Модуль 4: Node.js, Next.js и Angular /Модуль fs: обзор, синхронные и асинхронные методы

Модуль fs: обзор, синхронные и асинхронные методы

Модуль 4: Node.js, Next.js и Angular
3 уровень , 0 лекция
Открыта

1. Введение

Если вы когда-нибудь пытались прочитать или записать файл с помощью обычного JavaScript в браузере, то наверняка уже знаете: у браузера на это жёсткое табу. А вот Node.js, как настоящий серверный самурай, открывает для нас двери в мир файловой системы.

Модуль fs (от англ. file system) — это стандартный модуль Node.js, который позволяет работать с файлами и папками: читать, писать, удалять, создавать директории, копировать, перемещать и многое другое.

Факт: Если бы Node.js не умел работать с файлами, его бы никто не использовал для серверной разработки — ведь хранить данные где-то нужно!

Импортируем модуль fs

Начнём с самого простого. Чтобы использовать модуль fs, его нужно импортировать. В Node.js (CommonJS) это делается так:

const fs = require('fs');

Если вы используете ES Modules (например, с расширением .mjs или с "type": "module" в package.json):

import fs from 'fs';

В этой лекции мы будем использовать синтаксис CommonJS, так как он наиболее распространён для работы с fs.

2. Синхронные и асинхронные методы: в чём разница?

Модуль fs предоставляет две версии почти каждой функции:

  • Асинхронная (рекомендуемая) — обычно принимает колбэк или возвращает Promise (в промисовой версии, о ней поговорим позже).
  • Синхронная — выполняет операцию и блокирует поток до завершения.

Пример:
Асинхронно: fs.readFile(path, callback)
Синхронно: fs.readFileSync(path)

Почему это важно?

Node.js работает по однопоточному принципу: пока одна операция выполняется синхронно, остальные пользователи могут ждать в очереди. Поэтому для серверов и большинства приложений всегда используйте асинхронные методы! Синхронные — только для скриптов, которые запускаются разово (например, миграции или генерация файлов).

Ниже в этой лекции мы только познакомимся с различными методами модуля fs. А в следующих лекциях разберем их подробно.

3. Чтение файлов: асинхронно и синхронно

Асинхронное чтение файла

Давайте попробуем прочитать файл hello.txt (создайте его заранее и напишите туда что-нибудь приятное):


const fs = require('fs');

fs.readFile('hello.txt', 'utf8', (err, data) => {
  if (err) {
    console.error('Ошибка при чтении файла:', err);
    return;
  }
  console.log('Содержимое файла:', data);
});

Разбор кода:

  • 'hello.txt' — путь к файлу (можно относительный или абсолютный).
  • 'utf8' — кодировка (иначе получите Buffer, а не строку).
  • Колбэк принимает два аргумента: err (ошибка, если что-то пошло не так) и data (содержимое файла).

Аналогия: Асинхронное чтение — как если бы вы заказали пиццу и продолжили смотреть сериал, а курьер позвонит, когда привезёт.

Синхронное чтение файла


const fs = require('fs');

try {
  const data = fs.readFileSync('hello.txt', 'utf8');
  console.log('Содержимое файла:', data);
} catch (err) {
  console.error('Ошибка при чтении файла:', err);
}

Здесь выполнение программы останавливается на момент чтения файла, пока операция не завершится. Если файла нет — будет выброшено исключение, поэтому используем try...catch.

4. Запись файлов: асинхронно и синхронно

Асинхронная запись файла


const fs = require('fs');

fs.writeFile('output.txt', 'Привет, Node.js!', 'utf8', (err) => {
  if (err) {
    console.error('Ошибка при записи файла:', err);
    return;
  }
  console.log('Файл успешно записан!');
});
  • Если файла не было — он будет создан.
  • Если файл был — его содержимое перезапишется.

Синхронная запись файла


const fs = require('fs');

try {
  fs.writeFileSync('output.txt', 'Привет, Node.js!', 'utf8');
  console.log('Файл успешно записан!');
} catch (err) {
  console.error('Ошибка при записи файла:', err);
}

5. Добавление данных в файл: appendFile и appendFileSync

Иногда нужно не перезаписывать файл, а добавить к нему новые данные — например, для логов.

Асинхронно


const fs = require('fs');

fs.appendFile('log.txt', 'Новая запись\n', 'utf8', (err) => {
  if (err) {
    console.error('Ошибка при добавлении в файл:', err);
    return;
  }
  console.log('Запись добавлена в log.txt');
});

Синхронно


const fs = require('fs');

try {
  fs.appendFileSync('log.txt', 'Новая запись\n', 'utf8');
  console.log('Запись добавлена в log.txt');
} catch (err) {
  console.error('Ошибка при добавлении в файл:', err);
}

6. Проверка существования файла: fs.existsSync и fs.access

Синхронная проверка


const fs = require('fs');

if (fs.existsSync('hello.txt')) {
  console.log('Файл существует!');
} else {
  console.log('Файл не найден!');
}

Асинхронная проверка (современный способ)

Метод fs.exists считается устаревшим. Используйте fs.access:


const fs = require('fs');

fs.access('hello.txt', fs.constants.F_OK, (err) => {
  if (err) {
    console.log('Файл не найден!');
  } else {
    console.log('Файл существует!');
  }
});

7. Удаление файлов: unlink и unlinkSync

Асинхронно


const fs = require('fs');

fs.unlink('output.txt', (err) => {
  if (err) {
    console.error('Ошибка при удалении файла:', err);
    return;
  }
  console.log('Файл удалён!');
});

Синхронно


const fs = require('fs');

try {
  fs.unlinkSync('output.txt');
  console.log('Файл удалён!');
} catch (err) {
  console.error('Ошибка при удалении файла:', err);
}

8. Пример: мини-логгер на Node.js

Давайте напишем простую функцию для логирования сообщений в файл. Это будет маленькая, но полезная часть нашего будущего приложения.


const fs = require('fs');

/**
 * Добавляет строку в лог-файл с текущей датой и временем.
 * @param {string} message Сообщение для лога
 */
function logMessage(message) {
  const now = new Date().toISOString();
  const logLine = `[${now}] ${message}\n`;

  fs.appendFile('app.log', logLine, 'utf8', (err) => {
    if (err) {
      console.error('Ошибка при логировании:', err);
    }
  });
}

// Пример использования:
logMessage('Приложение запущено');
logMessage('Что-то произошло...');

Совет: Не используйте синхронные методы для логирования в настоящих приложениях — иначе сервер может "зависнуть" на записи!

9. Особенности путей: относительный и абсолютный

Если указать только имя файла (например, 'hello.txt'), путь считается относительным к рабочей директории (обычно это папка, из которой вы запустили Node.js).

Чтобы избежать путаницы, используйте модуль path и переменную __dirname:


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

const filePath = path.join(__dirname, 'hello.txt');
fs.readFile(filePath, 'utf8', (err, data) => {
  // ...
});

10. Асинхронность: почему это важно для серверов

Когда вы используете асинхронные методы, Node.js не блокирует выполнение программы. Это особенно важно для серверов, которые должны быстро отвечать многим пользователям одновременно.

Пример:
Если вы используете fs.readFileSync в обработчике HTTP-запроса, то пока файл читается, сервер не сможет обрабатывать другие запросы. Если пользователей много — все будут ждать, пока закончится операция.

11. Типичные ошибки при работе с модулем fs

Ошибка №1: Использование синхронных методов в серверном коде.
Синхронные методы блокируют весь поток Node.js, что приводит к "тормозам" при большом количестве запросов. Используйте их только для мелких скриптов или в инициализации.

Ошибка №2: Необработка ошибок в колбэках.
Если забыть проверить аргумент err в асинхронных методах, можно не заметить, что файл не был прочитан или записан. Всегда обрабатывайте ошибку — хотя бы выводите её в консоль.

Ошибка №3: Ожидание, что файл всегда будет существовать.
Не проверяя существование файла перед чтением, вы рискуете получить ошибку и "упавшее" приложение. Лучше использовать fs.access или ловить ошибку в колбэке/try-catch.

Ошибка №4: Неправильная работа с путями.
Использование относительных путей без понимания, где находится рабочая директория, часто приводит к "файл не найден". Используйте path.join(__dirname, ...) для надёжности.

Ошибка №5: Одновременная запись в один и тот же файл.
Если несколько асинхронных операций пишут в один файл одновременно, можно получить "кашу" или потерю данных. Для логов используйте очереди или специальные библиотеки, если нагрузка большая.

1
Задача
Модуль 4: Node.js, Next.js и Angular, 3 уровень, 0 лекция
Недоступна
Асинхронное чтение содержимого файла
Асинхронное чтение содержимого файла
1
Задача
Модуль 4: Node.js, Next.js и Angular, 3 уровень, 0 лекция
Недоступна
Асинхронная запись и последующее удаление файла
Асинхронная запись и последующее удаление файла
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ