1. Введение
В отличие от браузерного JavaScript, который работает в "песочнице" и не может напрямую читать файлы на диске пользователя, Node.js — это настоящий "бэкэнд", который может работать с файлами так же, как программы на Python, C# или Java. Вы можете:
- Читать текстовые конфиги, шаблоны, данные;
- Загружать изображения, логи, CSV, JSON и любые другие файлы;
- Реализовывать серверы, которые отдают статические файлы (например, картинки или HTML);
- Создавать утилиты для поиска, анализа или обработки файлов.
Всё это делает работу с файлами одной из ключевых задач любого node-приложения.
Асинхронное и синхронное чтение файлов: в чём разница?
Node.js — асинхронная среда, поэтому почти все операции с файлами можно делать двумя способами:
- Асинхронно — не блокирует Event Loop, позволяет обрабатывать другие запросы, пока файл читается. Используется почти всегда в продакшене и на сервере.
- Синхронно — блокирует выполнение программы до окончания операции. Удобно для простых скриптов, тестов, CLI-утилит, но не для серверов!
Аналогия: асинхронное чтение — вы дали поручение ассистенту и продолжаете работать; синхронное — вы стоите и ждёте, пока ассистент не закончит.
2. fs.readFile: асинхронное чтение файлов
Сигнатура и базовый пример
Асинхронный метод чтения файла:
fs.readFile(path, [options], callback)
- path — путь к файлу (строка или объект Buffer/URL)
- options — опционально: кодировка ('utf8', 'base64', и т.д.) или объект с доп. параметрами
- callback — функция, вызывается после чтения файла: function(err, data)
Простой пример: читаем файл с текстом
Допустим, у нас есть файл hello.txt с содержимым:
Привет, мир!
Читаем и выводим его содержимое:
const fs = require('fs');
fs.readFile('hello.txt', 'utf8', (err, data) => {
if (err) {
// Если произошла ошибка (например, файл не найден)
console.error('Ошибка при чтении файла:', err.message);
return;
}
// Если всё хорошо, выводим содержимое
console.log('Содержимое файла:', data);
});
Что здесь происходит?
- Node.js начинает читать файл, не блокируя основной поток.
- Как только чтение завершено, вызывается колбэк.
- Если возникла ошибка (например, файла нет), первый аргумент err будет объектом ошибки.
- Если всё ок, err будет null, а data — содержимое файла.
Важно: кодировка
Если не указать 'utf8', то по умолчанию вы получите Buffer (сырые байты):
fs.readFile('hello.txt', (err, data) => {
if (err) throw err;
console.log(data); // <Buffer 50 72 69 ...>
console.log(data.toString()); // Привет, мир!
});
Лучше всегда указывать кодировку, если читаете текст.
3. fs.readFileSync: синхронное чтение файлов
Сигнатура и базовый пример
Синхронный метод чтения файла:
const data = fs.readFileSync(path, [options]);
- path — путь к файлу
- options — кодировка или объект с параметрами (как и в асинхронном варианте)
Пример: читаем файл синхронно
const fs = require('fs');
try {
const data = fs.readFileSync('hello.txt', 'utf8');
console.log('Содержимое файла:', data);
} catch (err) {
console.error('Ошибка при чтении файла:', err.message);
}
В чём отличие?
- Весь файл читается сразу, выполнение кода останавливается до окончания операции.
- Если возникла ошибка, выбрасывается исключение — используем try/catch.
- Удобно для скриптов, не для серверов!
4. Когда использовать readFile, а когда readFileSync?
fs.readFile — используйте почти всегда, особенно если пишете сервер или приложение, где важна производительность. Не блокирует Event Loop, позволяет обслуживать других пользователей.
fs.readFileSync — используйте только в простых CLI-утилитах, скриптах, тестах, где блокировка — не проблема.
Если вы напишите сервер с fs.readFileSync внутри обработчика запроса, при каждом обращении к файлу сервер "замрёт" до окончания чтения. Представьте, что очередь в Макдональдсе останавливается каждый раз, пока кассир ищет сдачу — неэффективно!
5. Примеры: читаем JSON и обрабатываем ошибки
Чтение JSON-файла (асинхронно)
Допустим, у нас есть файл user.json:
{
"name": "Alice",
"age": 25
}
Читаем и парсим его:
const fs = require('fs');
fs.readFile('user.json', 'utf8', (err, data) => {
if (err) {
console.error('Ошибка:', err.message);
return;
}
try {
const user = JSON.parse(data);
console.log('Имя пользователя:', user.name);
console.log('Возраст:', user.age);
} catch (parseErr) {
console.error('Ошибка парсинга JSON:', parseErr.message);
}
});
Чтение JSON-файла (синхронно)
const fs = require('fs');
try {
const data = fs.readFileSync('user.json', 'utf8');
const user = JSON.parse(data);
console.log('Имя пользователя:', user.name);
console.log('Возраст:', user.age);
} catch (err) {
console.error('Ошибка:', err.message);
}
6. Полезные нюансы
Как читать файл из поддиректории
Если ваш файл лежит в папке data/hello.txt, используйте относительный путь:
fs.readFile('data/hello.txt', 'utf8', (err, data) => { ... });
Совет: Для кроссплатформенных путей используйте модуль path:
const path = require('path');
const filePath = path.join(__dirname, 'data', 'hello.txt');
fs.readFile(filePath, 'utf8', (err, data) => { ... });
Краткое сравнение: асинхронное vs синхронное чтение
| fs.readFile (асинхронно) | fs.readFileSync (синхронно) | |
|---|---|---|
| Блокирует Event Loop? | Нет | Да |
| Обработка ошибок | err в колбэке | try/catch |
| Где использовать | Сервер, веб-приложения | Скрипты, CLI, тесты |
| Пример | |
|
Размер файла
fs.readFile и fs.readFileSync читают весь файл целиком в память. Для больших файлов (гигабайты!) используйте потоки (fs.createReadStream), иначе у вас может закончиться память.
Асинхронность и порядок выполнения
Если после fs.readFile сразу написать console.log('Готово!'), то это сообщение появится до вывода содержимого файла, потому что чтение файла — асинхронное:
fs.readFile('hello.txt', 'utf8', (err, data) => {
if (!err) console.log(data);
});
console.log('Готово!'); // это появится раньше
7. Практика: читаем файл с задачами
Давайте продолжим развивать наше учебное приложение — простой менеджер задач, который хранит список задач в текстовом файле tasks.txt, каждая задача — на отдельной строке.
Пример содержимого tasks.txt:
Купить хлеб
Позвонить маме
Сделать домашку по Node.js
Код для чтения и вывода всех задач:
const fs = require('fs');
fs.readFile('tasks.txt', 'utf8', (err, data) => {
if (err) {
console.error('Не удалось прочитать файл с задачами:', err.message);
return;
}
// Разбиваем содержимое файла на строки
const tasks = data.split('\n').filter(line => line.trim() !== '');
console.log('Ваши задачи:');
tasks.forEach((task, idx) => {
console.log(`${idx + 1}. ${task}`);
});
});
Что происходит?
- Читаем файл асинхронно.
- Если ошибка — выводим сообщение.
- Если успех — разбиваем содержимое по строкам и выводим список задач с номерами.
8. Типичные ошибки при чтении файлов
Ошибка №1: забыли обработать ошибку в колбэке.
Если не проверить err после fs.readFile, программа может "тихо" упасть или повести себя странно. Всегда проверяйте err первым делом!
Ошибка №2: используете fs.readFileSync в серверном коде.
Скрипт может "заморозить" сервер для всех пользователей. Помните: синхронные методы — только для CLI или тестов.
Ошибка №3: забыли указать кодировку.
Если не указать 'utf8', получите Buffer вместо строки, что приведёт к загадочным ошибкам при работе с текстом.
Ошибка №4: неверный путь к файлу.
Часто новички пишут относительный путь, не учитывая, что скрипт может запускаться из другой директории. Используйте __dirname и модуль path для надёжности.
Ошибка №5: не обрабатываете ошибки парсинга JSON.
Если файл повреждён, JSON.parse выбросит исключение — всегда используйте try/catch вокруг парсинга.
Ошибка №6: пытаетесь читать огромный файл через readFile.
Для больших файлов используйте потоки (fs.createReadStream), иначе закончится память.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ