1. Введение
В реальных приложениях запись в файл нужна постоянно: сохранять результаты работы, логи, пользовательские данные, кэш, настройки и многое другое. Если вы когда-нибудь писали заметку в текстовый редактор или сохраняли картинку из Paint, то вы уже пользовались записью в файл — просто не через Node.js. Теперь мы научимся делать это программно.
Node.js предоставляет нам два основных метода для записи файлов:
- fs.writeFile — асинхронный метод (рекомендуется для большинства случаев)
- fs.writeFileSync — синхронный метод (для простых скриптов и особых случаев)
Оба этих метода позволяют создать новый файл или перезаписать существующий. Если файл не существует — он будет создан. Если существует — его содержимое будет полностью заменено новым. (Осторожно: старые данные исчезнут — как будто вы залили новый чай в уже наполненную чашку, не вылив старый. Надо быть аккуратнее!)
2. fs.writeFile: асинхронная запись
Синтаксис
fs.writeFile(path, data, [options], callback)
- path — путь к файлу (строка или Buffer)
- data — что записываем (строка или Buffer)
- options — объект с настройками (необязателен)
- callback — функция, которая вызовется по завершении (обязательна!)
Пример: создаём файл с приветствием
const fs = require('fs');
fs.writeFile('hello.txt', 'Привет, мир!', (err) => {
if (err) {
console.error('Ошибка при записи файла:', err);
} else {
console.log('Файл успешно записан!');
}
});
Что тут происходит?
Node.js начинает записывать строку 'Привет, мир!' в файл hello.txt. Как только операция завершится (успешно или с ошибкой), вызывается функция-обработчик. Если всё прошло хорошо — увидим сообщение об успехе. Если что-то пошло не так (например, нет прав на запись) — ошибка попадёт в err.
Асинхронность: почему это важно?
Вспомните: Node.js работает в одном потоке, и если вы будете долго писать в файл синхронно, всё приложение «замрёт». Асинхронный метод позволяет продолжать выполнять другие задачи, пока файл записывается. Это особенно важно для серверов, которые должны быстро отвечать на запросы.
3. fs.writeFileSync: синхронная запись
Синтаксис
fs.writeFileSync(path, data, [options])
- Всё как у асинхронного варианта, только без колбэка
- Если что-то пошло не так — будет выброшено исключение (try/catch наше всё!)
Пример: записываем файл синхронно
const fs = require('fs');
try {
fs.writeFileSync('hello_sync.txt', 'Синхронная запись!');
console.log('Файл записан синхронно!');
} catch (err) {
console.error('Ошибка при записи файла:', err);
}
Здесь всё просто: если запись прошла успешно — увидим сообщение, если нет — поймаем ошибку через catch. Но помните: пока файл пишется, Node.js не делает больше ничего! Поэтому используем этот метод только в коротких скриптах или там, где блокировка не страшна (например, в миграциях или CLI-утилитах).
4. Практика: сохраняем заметки пользователя
Давайте продолжим развивать наше учебное приложение — простую консольную заметочную книжку. На прошлой лекции мы научились читать файл с заметками. Теперь реализуем функцию сохранения новой заметки.
Шаг 1. Добавим функцию для записи новой заметки
const fs = require('fs');
const path = require('path');
const NOTES_FILE = path.join(__dirname, 'notes.txt');
function addNote(note, callback) {
// Сохраняем заметку в конец файла, добавляя перевод строки
fs.appendFile(NOTES_FILE, note + '\n', (err) => {
if (err) {
callback(err);
} else {
callback(null);
}
});
}
Внимание: Здесь мы используем fs.appendFile, чтобы не перезаписывать файл целиком, а добавлять новые заметки в конец. Но если вы хотите полностью заменить содержимое файла, используйте fs.writeFile.
Шаг 2. Используем функцию
addNote('Купить молоко', (err) => {
if (err) {
console.error('Не удалось сохранить заметку:', err);
} else {
console.log('Заметка сохранена!');
}
});
Шаг 3. Проверяем результат
Если вы откроете файл notes.txt, увидите в нём вашу новую заметку. Каждый вызов addNote добавляет строку в конец файла.
5. Передача опций: кодировка, флаги, права доступа
Методы fs.writeFile и fs.writeFileSync принимают третий необязательный параметр — объект опций. Самые часто используемые:
- encoding — кодировка (по умолчанию 'utf8')
- mode — права доступа (по умолчанию 0o666)
- flag — как открывать файл ('w' — запись, 'a' — добавление, и др.)
Пример: записываем файл в другой кодировке
fs.writeFile('data.txt', 'Данные в windows-1251', { encoding: 'windows-1251' }, (err) => {
if (err) throw err;
console.log('Файл сохранён в кодировке windows-1251!');
});
Пример: добавляем в файл (флаг 'a')
fs.writeFile('log.txt', 'Запись лога\n', { flag: 'a' }, (err) => {
if (err) throw err;
console.log('Лог добавлен!');
});
Таблица популярных флагов
| Флаг | Описание |
|---|---|
|
Запись (создать/перезаписать) |
|
Добавление (append) |
|
Запись только если файла нет |
|
Добавление только если файла нет |
6. Как узнать, что файл записан? Обработка ошибок
Асинхронный метод всегда требует колбэк-функцию. Никогда не игнорируйте ошибку — иначе потом будете долго искать, почему файл не сохранился (или сохранился не туда).
Пример: ловим ошибку записи
fs.writeFile('/root/protected.txt', 'Тест', (err) => {
if (err) {
// Например, нет прав на запись
console.error('Ошибка:', err.message);
} else {
console.log('Файл записан!');
}
});
Пример: try/catch для синхронного метода
try {
fs.writeFileSync('/root/protected.txt', 'Тест');
} catch (err) {
console.error('Ошибка при синхронной записи:', err.message);
}
7. Полезные нюансы
Работа с объектами: запись JSON
В реальных приложениях часто нужно сохранять не просто текст, а структуру данных. Для этого используют сериализацию в JSON.
Пример: сохраняем объект пользователя
const user = {
name: 'Иван',
age: 30,
email: 'ivan@example.com'
};
fs.writeFile('user.json', JSON.stringify(user, null, 2), (err) => {
if (err) throw err;
console.log('Пользователь сохранён в user.json!');
});
JSON.stringify(obj, null, 2) делает красивый отступ (2 пробела) для читаемости.
Перезапись файла
fs.writeFile и fs.writeFileSync по умолчанию полностью затирают старое содержимое файла. Если вы хотите сохранить старые данные, используйте fs.appendFile или опцию { flag: 'a' }.
Размер файла
Нет ограничений на размер данных, но для больших файлов лучше использовать потоки (fs.createWriteStream) — об этом будет отдельная лекция.
Запись бинарных данных
Можно писать не только строки, но и Buffer'ы (например, картинки):
const imageBuffer = ...; // получили из другого источника
fs.writeFile('image.png', imageBuffer, (err) => {
if (err) throw err;
console.log('Картинка сохранена!');
});
8. Типичные ошибки при работе с записью файлов
Очень часто новички забывают проверить, что вернулось в err в колбэке или не оборачивают синхронную запись в try/catch. В результате ошибки записи остаются незамеченными, а данные теряются.
Если вы хотите добавить данные в файл, но используете fs.writeFile без флага 'a' или fs.appendFile, старое содержимое будет уничтожено. Не забывайте про флаги!
Вызов fs.writeFileSync в обработчике HTTP-запроса может «заморозить» сервер для всех пользователей, пока файл пишется. Используйте синхронные методы только там, где это действительно оправдано.
Забыли сериализовать объект в JSON? В результате в файле окажется [object Object] вместо ожидаемой структуры. Используйте JSON.stringify для объектов.
Если записываете не-UTF8 текст, обязательно указывайте нужную кодировку. Иначе потом будете видеть кракозябры вместо кириллицы.
Если путь к файлу включает несуществующую папку, запись завершится ошибкой. Проверьте, что все папки существуют, или создайте их заранее (fs.mkdir).
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ