- Home »

Работа с файлами через модуль fs в Node.js
Работа с файлами — это хлеб и масло любого серверного разработчика. Если ты разворачиваешь проекты на VPS, пишешь скрипты для автоматизации или просто управляешь файлами через Node.js, то модуль fs станет твоим верным спутником. Сегодня разберём, как эффективно использовать fs для решения повседневных задач — от чтения логов до массового обновления конфигураций.
Как работает модуль fs
Модуль fs (file system) — это встроенный модуль Node.js, который предоставляет API для работы с файловой системой. Он имеет синхронные и асинхронные методы для всех основных операций.
Основные принципы работы:
- Асинхронность по умолчанию — большинство методов неблокирующие
- Синхронные аналоги — с суффиксом Sync
- Promise-based API — через fs.promises
- Стримы — для работы с большими файлами
Подключение модуля:
const fs = require('fs');
const fsPromises = require('fs').promises;
// или для современных версий Node.js
import fs from 'fs';
import { promises as fsPromises } from 'fs';
Базовые операции с файлами
Чтение файлов
Три основных способа чтения файлов:
// Асинхронное чтение с callback
fs.readFile('config.json', 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});
// Синхронное чтение (блокирующее)
try {
const data = fs.readFileSync('config.json', 'utf8');
console.log(data);
} catch (err) {
console.error(err);
}
// С промисами (рекомендуется)
async function readConfig() {
try {
const data = await fsPromises.readFile('config.json', 'utf8');
return JSON.parse(data);
} catch (err) {
console.error('Ошибка чтения файла:', err);
}
}
Запись файлов
Создание и перезапись файлов:
// Запись с промисами
async function writeConfig(config) {
try {
await fsPromises.writeFile('config.json', JSON.stringify(config, null, 2));
console.log('Конфиг сохранён');
} catch (err) {
console.error('Ошибка записи:', err);
}
}
// Дописывание в файл
async function appendToLog(message) {
const logEntry = `${new Date().toISOString()} - ${message}\n`;
await fsPromises.appendFile('app.log', logEntry);
}
Продвинутые операции
Работа с директориями
Создание структуры директорий:
// Создание директории рекурсивно
async function createProjectStructure(projectName) {
const dirs = [
`${projectName}/src`,
`${projectName}/tests`,
`${projectName}/config`,
`${projectName}/logs`
];
for (const dir of dirs) {
await fsPromises.mkdir(dir, { recursive: true });
}
}
Чтение содержимого директории
// Получение списка файлов с детальной информацией
async function getFileList(directory) {
try {
const files = await fsPromises.readdir(directory, { withFileTypes: true });
const fileInfo = files.map(file => ({
name: file.name,
isDirectory: file.isDirectory(),
isFile: file.isFile()
}));
return fileInfo;
} catch (err) {
console.error('Ошибка чтения директории:', err);
}
}
Практические примеры и кейсы
Скрипт для бэкапа конфигураций
Полезный скрипт для создания резервных копий конфигов на сервере:
const fs = require('fs').promises;
const path = require('path');
async function backupConfigs() {
const configPaths = [
'/etc/nginx/nginx.conf',
'/etc/apache2/apache2.conf',
'/etc/mysql/my.cnf'
];
const backupDir = `./backups/${new Date().toISOString().split('T')[0]}`;
await fs.mkdir(backupDir, { recursive: true });
for (const configPath of configPaths) {
try {
const content = await fs.readFile(configPath, 'utf8');
const fileName = path.basename(configPath);
await fs.writeFile(`${backupDir}/${fileName}`, content);
console.log(`✓ Скопирован ${configPath}`);
} catch (err) {
console.error(`✗ Ошибка копирования ${configPath}:`, err.message);
}
}
}
Мониторинг файлов
Отслеживание изменений в файлах:
const fs = require('fs');
// Простой мониторинг файла
fs.watchFile('config.json', (curr, prev) => {
console.log(`Файл изменён: ${prev.mtime} -> ${curr.mtime}`);
});
// Мониторинг директории
fs.watch('./logs', (eventType, filename) => {
if (eventType === 'change') {
console.log(`Файл ${filename} изменён`);
}
});
Сравнение подходов
Метод | Преимущества | Недостатки | Использование |
---|---|---|---|
Callback | Классический подход, широкая поддержка | Callback hell, сложность обработки ошибок | Легаси проекты |
Sync | Простота, линейный код | Блокирует event loop | Инициализация, CLI утилиты |
Promises | Современный подход, async/await | Требует Node.js 10+ | Рекомендуется для новых проектов |
Streams | Низкое потребление памяти | Сложность для простых задач | Большие файлы |
Работа с большими файлами
Для работы с большими файлами используй стримы:
const fs = require('fs');
const readline = require('readline');
// Чтение больших логов построчно
async function processLargeLog(filepath) {
const fileStream = fs.createReadStream(filepath);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
let errorCount = 0;
for await (const line of rl) {
if (line.includes('ERROR')) {
errorCount++;
}
}
console.log(`Найдено ошибок: ${errorCount}`);
}
Автоматизация с fs
Массовое переименование файлов
async function renameFiles(directory, pattern, replacement) {
const files = await fs.readdir(directory);
for (const file of files) {
if (file.includes(pattern)) {
const oldPath = path.join(directory, file);
const newPath = path.join(directory, file.replace(pattern, replacement));
await fs.rename(oldPath, newPath);
console.log(`${file} -> ${path.basename(newPath)}`);
}
}
}
Очистка старых логов
async function cleanOldLogs(logsDir, daysOld = 30) {
const files = await fs.readdir(logsDir);
const cutoffDate = new Date(Date.now() - (daysOld * 24 * 60 * 60 * 1000));
for (const file of files) {
const filePath = path.join(logsDir, file);
const stats = await fs.stat(filePath);
if (stats.mtime < cutoffDate) {
await fs.unlink(filePath);
console.log(`Удалён старый лог: ${file}`);
}
}
}
Интеграция с другими пакетами
Модуль fs отлично работает с другими популярными пакетами:
- chokidar — продвинутый file watcher
- fs-extra — расширенные возможности fs
- glob — поиск файлов по паттернам
- graceful-fs — более надёжная работа с файлами
Пример с fs-extra:
const fse = require('fs-extra');
// Копирование директории со всем содержимым
await fse.copy('./source', './destination');
// Создание JSON файла
await fse.outputJson('./config/settings.json', { port: 3000 });
// Проверка существования файла
const exists = await fse.pathExists('./config.json');
Обработка ошибок
Правильная обработка ошибок критически важна:
async function safeFileOperation(filepath) {
try {
// Проверяем доступность файла
await fs.access(filepath, fs.constants.F_OK);
const data = await fs.readFile(filepath, 'utf8');
return data;
} catch (err) {
switch (err.code) {
case 'ENOENT':
console.error('Файл не найден:', filepath);
break;
case 'EACCES':
console.error('Нет прав доступа:', filepath);
break;
case 'EMFILE':
console.error('Слишком много открытых файлов');
break;
default:
console.error('Неизвестная ошибка:', err);
}
throw err;
}
}
Производительность и оптимизация
Несколько советов для оптимизации работы с файлами:
- Используй стримы для больших файлов
- Кэшируй результаты fs.stat для часто проверяемых файлов
- Группируй операции — лучше прочитать файл один раз
- Используй fs.promises вместо callback'ов
Пример кэширования:
const statCache = new Map();
async function getCachedStat(filepath) {
if (statCache.has(filepath)) {
const cached = statCache.get(filepath);
if (Date.now() - cached.timestamp < 60000) { // 1 минута
return cached.stats;
}
}
const stats = await fs.stat(filepath);
statCache.set(filepath, { stats, timestamp: Date.now() });
return stats;
}
Заключение
Модуль fs — это мощный инструмент для работы с файловой системой в Node.js. Он особенно полезен для серверных задач: автоматизации развёртывания, обработки логов, управления конфигурациями. Если ты работаешь с серверами — будь то VPS или выделенный сервер — навыки работы с fs существенно упростят повседневные задачи.
Главные рекомендации:
- Используй fs.promises для нового кода
- Всегда обрабатывай ошибки правильно
- Для больших файлов применяй стримы
- Кэшируй результаты частых операций
- Изучи fs-extra для расширенных возможностей
Дополнительные ресурсы:
В этой статье собрана информация и материалы из различных интернет-источников. Мы признаем и ценим работу всех оригинальных авторов, издателей и веб-сайтов. Несмотря на то, что были приложены все усилия для надлежащего указания исходного материала, любая непреднамеренная оплошность или упущение не являются нарушением авторских прав. Все упомянутые товарные знаки, логотипы и изображения являются собственностью соответствующих владельцев. Если вы считаете, что какой-либо контент, использованный в этой статье, нарушает ваши авторские права, немедленно свяжитесь с нами для рассмотрения и принятия оперативных мер.
Данная статья предназначена исключительно для ознакомительных и образовательных целей и не ущемляет права правообладателей. Если какой-либо материал, защищенный авторским правом, был использован без должного упоминания или с нарушением законов об авторском праве, это непреднамеренно, и мы исправим это незамедлительно после уведомления. Обратите внимание, что переиздание, распространение или воспроизведение части или всего содержимого в любой форме запрещено без письменного разрешения автора и владельца веб-сайта. Для получения разрешений или дополнительных запросов, пожалуйста, свяжитесь с нами.