1. Создание директорий: fs.mkdir и fs.mkdirSync
Файлы — это хорошо, но в реальных приложениях редко обходится без папок. Например, вы хотите сохранять фотографии пользователей в отдельные папки, создавать резервные копии, автоматически чистить временные каталоги или строить структуру проекта на лету. Всё это требует умения создавать, удалять и, иногда, проверять существование директорий.
Node.js предоставляет для этого несколько методов в модуле fs. Давайте разберём их на практике.
Асинхронный способ: fs.mkdir
Асинхронный метод fs.mkdir позволяет создать новую директорию. Пример:
const fs = require('fs');
const path = require('path');
const dirPath = path.join(__dirname, 'uploads');
fs.mkdir(dirPath, (err) => {
if (err) {
console.error('Ошибка при создании папки:', err);
return;
}
console.log('Папка успешно создана!');
});
Что здесь происходит?
- Мы формируем путь к папке 'uploads' относительно текущей директории.
- Затем вызываем fs.mkdir, который создаёт папку и вызывает callback-функцию.
- Если папка уже существует, будет ошибка (EEXIST).
Синхронный способ: fs.mkdirSync
Если вы пишете скрипт, где блокировка потока некритична (например, при инициализации проекта), можно использовать синхронный вариант:
const fs = require('fs');
const path = require('path');
const dirPath = path.join(__dirname, 'uploads');
try {
fs.mkdirSync(dirPath);
console.log('Папка успешно создана (sync)!');
} catch (err) {
if (err.code === 'EEXIST') {
console.log('Папка уже существует');
} else {
console.error('Ошибка при создании папки:', err);
}
}
Создание вложенных директорий: опция { recursive: true }
По умолчанию, если вы попытаетесь создать путь вроде 'a/b/c', а папки 'a' и 'b' не существуют, получите ошибку. Чтобы Node.js создал всю цепочку папок, используйте опцию { recursive: true }:
const nestedPath = path.join(__dirname, 'photos', '2024', '06');
fs.mkdir(nestedPath, { recursive: true }, (err) => {
if (err) throw err;
console.log('Вложенные папки созданы!');
});
Точно так же и для синхронного варианта:
fs.mkdirSync(nestedPath, { recursive: true });
Факт:
Если папка уже существует, и вы используете { recursive: true }, ошибки не будет — Node.js просто молча скажет "ОК".
2. Удаление директорий: fs.rmdir, fs.rm, fs.rmdirSync, fs.rmSync
Старый добрый fs.rmdir (устаревший!)
Раньше для удаления папки использовали fs.rmdir. Но этот метод устарел (deprecated) и не работает с непустыми папками — вы получите ошибку, если в директории есть хоть что-то.
fs.rmdir(dirPath, (err) => {
if (err) {
console.error('Ошибка при удалении папки:', err);
return;
}
console.log('Папка удалена!');
});
Важно:
fs.rmdir удаляет только пустые папки. Для современных приложений используйте fs.rm!
Новый способ: fs.rm и { recursive: true }
Появившийся в Node.js 14.14.0 метод fs.rm позволяет удалять и файлы, и папки (в том числе непустые!). Это новый стандарт.
// Удалить папку (и всё её содержимое)
fs.rm(dirPath, { recursive: true, force: true }, (err) => {
if (err) {
console.error('Ошибка при удалении папки:', err);
return;
}
console.log('Папка полностью удалена!');
});
- { recursive: true } — удаляет папку со всем содержимым.
- { force: true } — не выбрасывает ошибку, если папки нет (аналогично rm -rf в Unix).
Синхронный вариант: fs.rmSync
try {
fs.rmSync(dirPath, { recursive: true, force: true });
console.log('Папка удалена (sync)!');
} catch (err) {
console.error('Ошибка при удалении папки:', err);
}
Почему fs.rm лучше?
- Удаляет не только пустые, но и непустые папки (и даже отдельные файлы).
- Позволяет настраивать поведение через опции.
- Современный подход, поддерживается и развивается.
3. Проверка существования директории
Перед созданием или удалением папки иногда хочется проверить, существует ли она. Для этого можно использовать fs.existsSync (синхронно) или fs.stat (асинхронно):
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath);
}
Асинхронно:
fs.stat(dirPath, (err, stats) => {
if (err) {
// Папки нет — можно создавать
fs.mkdir(dirPath, callback);
} else if (stats.isDirectory()) {
console.log('Папка уже есть');
}
});
Внимание:
fs.existsSync — синхронный и блокирует поток, поэтому используйте его только в простых скриптах или при инициализации, а не в серверном коде под нагрузкой.
4. Примеры: создание, удаление и повторное создание папки
Давайте свяжем всё в единое мини-приложение. Например, реализуем функцию "очистить папку и создать заново" — часто используется для временных директорий.
const fs = require('fs');
const path = require('path');
const tempDir = path.join(__dirname, 'temp');
// Удаляем папку, если есть
fs.rm(tempDir, { recursive: true, force: true }, (err) => {
if (err) {
console.error('Ошибка при удалении temp:', err);
return;
}
// Создаём снова
fs.mkdir(tempDir, (err) => {
if (err) {
console.error('Ошибка при создании temp:', err);
return;
}
console.log('Временная папка готова!');
});
});
Комментарии:
- Сначала удаляем папку (даже если она не существует — благодаря force: true).
- Потом создаём её заново.
5. Пример: создание вложенной структуры для проекта
Допустим, вы пишете генератор структуры проекта (например, мини-версию create-react-app). Нужно создать несколько вложенных папок:
const fs = require('fs');
const path = require('path');
const baseDir = path.join(__dirname, 'my-app');
const dirs = [
'src',
'src/components',
'src/styles',
'public',
'tests'
];
dirs.forEach((dir) => {
const fullPath = path.join(baseDir, dir);
fs.mkdir(fullPath, { recursive: true }, (err) => {
if (err) {
console.error(`Ошибка при создании ${fullPath}:`, err);
} else {
console.log(`Создано: ${fullPath}`);
}
});
});
6. Полезные нюансы
Особенности и нюансы работы с директориями
- Если вы попытаетесь создать папку, которая уже существует, без опции { recursive: true }, будет ошибка.
- При удалении папки с помощью fs.rm и без опции { recursive: true }, если папка не пуста — будет ошибка.
- Старые методы (fs.rmdir, fs.rmdirSync) считаются устаревшими (deprecated) — не используйте их в новых проектах.
- Для асинхронных операций всегда обрабатывайте ошибки в callback или через промисы.
Работа с промисами: fs.promises.mkdir, fs.promises.rm
С приходом промисов жизнь стала проще (и чище). Вот как можно делать те же операции с использованием async/await:
const fs = require('fs').promises;
const path = require('path');
async function recreateDir(dirPath) {
try {
await fs.rm(dirPath, { recursive: true, force: true });
await fs.mkdir(dirPath, { recursive: true });
console.log('Директория полностью пересоздана!');
} catch (err) {
console.error('Ошибка:', err);
}
}
const dir = path.join(__dirname, 'cache');
recreateDir(dir);
7. Типичные ошибки при работе с директориями
Ошибка №1: попытка удалить непустую папку через fs.rmdir без опции recursive
Если использовать устаревший метод без нужных опций, получите ошибку ENOTEMPTY. Переходите на fs.rm!
Ошибка №2: забыли обработать ошибку "EEXIST" при создании папки
Если создать существующую папку без { recursive: true }, будет ошибка. Добавьте опцию — и Node.js молча "простит" вас.
Ошибка №3: блокировка потока из-за синхронных методов
fs.mkdirSync, fs.rmSync и им подобные блокируют основной поток. Не используйте их в серверном коде, где важна производительность.
Ошибка №4: не проверяете результат асинхронной операции
Асинхронные методы требуют обработки ошибок в callback или через catch для промисов. Не оставляйте ошибки без внимания — они могут "тихо" ломать логику вашего приложения.
Ошибка №5: путаница с путями на разных ОС
Используйте модуль path для формирования путей — это избавит от головной боли при переносе кода между Windows, Linux и Mac.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ