Как создать полный путь с помощью node fs.mkdirSync?
Я пытаюсь создать полный путь, если он не существует.
Код выглядит следующим образом:
var fs = require('fs');
if (!fs.existsSync(newDest)) fs.mkdirSync(newDest);
Этот код отлично работает, пока есть только один подкаталог (newDest, например 'dir1'), однако, когда есть путь к каталогу, подобный ('dir1/dir2'), он терпит неудачу с
Ошибка: ENOENT, нет такого файла или каталога
Я хотел бы иметь возможность создать полный путь с минимальным количеством строк кода.
Я читаю, что есть рекурсивный вариант fs и пробовал это как
var fs = require('fs');
if (!fs.existsSync(newDest)) fs.mkdirSync(newDest,'0777', true);
Я чувствую, что это должно быть так просто, чтобы рекурсивно создать каталог, который не существует. Я что-то пропустил или мне нужно проанализировать путь и проверить каждый каталог и создать его, если он еще не существует?
Я новичок в Node. Может быть, я использую старую версию FS?
Ответы
Ответ 1
Один из вариантов заключается в использовании shelljs module
npm install shelljs
var shell = require('shelljs');
shell.mkdir('-p', fullPath);
С этой страницы:
Доступные опции:
p: полный путь (при необходимости создаст промежуточные каталоги)
Как отмечали другие, есть и другие более целенаправленные модули. Но, вне mkdirp, у него есть множество других полезных операций с оболочкой (например, grep и т.д.), И он работает на windows и * nix
Ответ 2
редактировать
В NodeJS версии 10.12.0
добавлена встроенная поддержка mkdir
и mkdirSync
для рекурсивного создания каталога с опцией recursive: true
как mkdirSync
ниже:
fs.mkdirSync(targetDir, { recursive: true });
И если вы предпочитаете fs Promises API
, вы можете написать
fs.promises.mkdir(targetDir, { recursive: true });
Оригинальный ответ
Создавайте каталоги рекурсивно, если они не существуют! (Нулевые зависимости)
const fs = require('fs');
const path = require('path');
function mkDirByPathSync(targetDir, { isRelativeToScript = false } = {}) {
const sep = path.sep;
const initDir = path.isAbsolute(targetDir) ? sep : '';
const baseDir = isRelativeToScript ? __dirname : '.';
return targetDir.split(sep).reduce((parentDir, childDir) => {
const curDir = path.resolve(baseDir, parentDir, childDir);
try {
fs.mkdirSync(curDir);
} catch (err) {
if (err.code === 'EEXIST') { // curDir already exists!
return curDir;
}
// To avoid 'EISDIR' error on Mac and 'EACCES'-->'ENOENT' and 'EPERM' on Windows.
if (err.code === 'ENOENT') { // Throw the original parentDir error on curDir 'ENOENT' failure.
throw new Error('EACCES: permission denied, mkdir '${parentDir}'');
}
const caughtErr = ['EACCES', 'EPERM', 'EISDIR'].indexOf(err.code) > -1;
if (!caughtErr || caughtErr && curDir === path.resolve(targetDir)) {
throw err; // Throw if it just the last created dir.
}
}
return curDir;
}, initDir);
}
использование
// Default, make directories relative to current working directory.
mkDirByPathSync('path/to/dir');
// Make directories relative to the current script.
mkDirByPathSync('path/to/dir', {isRelativeToScript: true});
// Make directories with an absolute path.
mkDirByPathSync('/path/to/dir');
демонстрация
Попытайся!
Пояснения
- [ОБНОВЛЕНИЕ] Это решение обрабатывает специфичные для платформы ошибки, такие как
EISDIR
для Mac и EPERM
и EACCES
для Windows. Благодаря всем отчетным комментариям @PediT., @JohnQ, @deed02392, @robyoder и @Almenon. - Это решение обрабатывает как относительные, так и абсолютные пути. Благодаря комментарию @john.
- В случае относительных путей целевые каталоги будут созданы (разрешены) в текущем рабочем каталоге. Чтобы разрешить их относительно текущего
{isRelativeToScript: true}
dir, передайте {isRelativeToScript: true}
. - Использование
path.sep
и path.resolve()
, а не просто /
concatenation, позволяет избежать кросс-платформенных проблем. - Использование
fs.mkdirSync
и обработка ошибки с помощью try/catch
если выброшено для обработки условий гонки: другой процесс может добавить файл между вызовами fs.existsSync()
и fs.mkdirSync()
и fs.mkdirSync()
исключение. - Другой способ добиться этого - проверить, существует ли файл, а затем создать его, т.
if (!fs.existsSync(curDir) fs.mkdirSync(curDir);
. Но это if (!fs.existsSync(curDir) fs.mkdirSync(curDir);
который делает код уязвимым для гонки. Условия. Благодаря @GershomMaes комментарий о проверке существования каталога.
- Требуется Node v6 и новее для поддержки деструктуризации. (Если у вас есть проблемы с реализацией этого решения со старыми версиями Node, просто оставьте мне комментарий)
Ответ 3
Более надежным ответом является использование mkdirp.
var mkdirp = require('mkdirp');
mkdirp('/path/to/dir', function (err) {
if (err) console.error(err)
else console.log('dir created')
});
Затем перейдите к записи файла в полный путь с помощью:
fs.writeFile ('/path/to/dir/file.dat'....
Ответ 4
fs-extra добавляет методы файловой системы, которые не включены в собственный модуль fs. Это капля замены фс.
Установить fs-extra
$ npm install --save fs-extra
const fs = require("fs-extra");
// Make sure the output directory is there.
fs.ensureDirSync(newDest);
Есть варианты синхронизации и асинхронности.
https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureDir.md
Ответ 5
Используя сокращение, мы можем проверить, существует ли каждый путь и создать его, если это необходимо, и таким образом, я думаю, что это проще. Отредактировано, спасибо @Arvin, мы должны использовать path.sep, чтобы получить подходящий разделитель сегментов пути для конкретной платформы.
const path = require('path');
// Path separators could change depending on the platform
const pathToCreate = 'path/to/dir';
pathToCreate
.split(path.sep)
.reduce((prevPath, folder) => {
const currentPath = path.join(prevPath, folder, path.sep);
if (!fs.existsSync(currentPath)){
fs.mkdirSync(currentPath);
}
return currentPath;
}, '');
Ответ 6
Эта функция была добавлена в node.js в версии 10.12.0, поэтому так же просто, как передать опцию {recursive: true}
качестве второго аргумента в fs.mkdir()
. См. Пример в официальных документах.
Нет необходимости в внешних модулях или вашей собственной реализации.
Ответ 7
я знаю, что это старый вопрос, но nodejs v10.12.0 теперь поддерживает это изначально с recursive
опцией, установленной в true. fs.mkdir
// Creates /tmp/a/apple, regardless of whether '/tmp' and /tmp/a exist.
fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => {
if (err) throw err;
});
Ответ 8
Теперь с NodeJS> = 10.12.0
вы можете использовать fs.mkdirSync(path, { recursive: true })
fs.mkdirSync
Ответ 9
Пример для Windows (без дополнительных зависимостей и обработки ошибок)
const path = require('path');
const fs = require('fs');
let dir = "C:\\temp\\dir1\\dir2\\dir3";
function createDirRecursively(dir) {
if (!fs.existsSync(dir)) {
createDirRecursively(path.join(dir, ".."));
fs.mkdirSync(dir);
}
}
createDirRecursively(dir); //creates dir1\dir2\dir3 in C:\temp
Ответ 10
Вы можете использовать следующую функцию
const recursiveUpload = (путь: строка) => {const paths = path.split("/")
const fullPath = paths.reduce((accumulator, current) => {
fs.mkdirSync(accumulator)
return '${accumulator}/${current}'
})
fs.mkdirSync(fullPath)
return fullPath
}
Итак, что он делает:
- Создайте переменную
paths
, где он сохраняет каждый путь сам по себе как элемент массива. - Добавляет "/" в конце каждого элемента массива.
- Делает для цикла:
- Создает каталог из конкатенации элементов массива, индексы которых от 0 до текущей итерации. В принципе, он рекурсивный.
Надеюсь, это поможет!
Кстати, в Node v10.12.0 вы можете использовать создание рекурсивного пути, указав его как дополнительный аргумент.
fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => { if (err) throw err; });
https://nodejs.org/api/fs.html#fs_fs_mkdirsync_path_options
Ответ 11
Слишком много ответов, но здесь есть решение без рекурсии, которое работает путем разделения пути, а затем слева направо, его снова поддерживают
function mkdirRecursiveSync(path) {
let paths = path.split(path.delimiter);
let fullPath = '';
paths.forEach((path) => {
if (fullPath === '') {
fullPath = path;
} else {
fullPath = fullPath + '/' + path;
}
if (!fs.existsSync(fullPath)) {
fs.mkdirSync(fullPath);
}
});
};
Для тех, кто обеспокоен совместимостью Windows vs Linux, просто замените косую черту двойным обратным слэшем '\' в обоих случаях выше, но TBH мы говорим о командной строке node fs not windows, а первая довольно прощает, и вышеприведенный код будет просто работать Windows и более полная кросс-платформа.
Ответ 12
const fs = require('fs');
try {
fs.mkdirSync(path, { recursive: true });
} catch (error) {
// this make script keep running, even when folder already exist
console.log(error);
}
Ответ 13
Вы можете просто проверить наличие папки или нет в пути рекурсивно и сделать папку, когда вы проверяете, не присутствуют ли они. (НЕТ ВНЕШНЕЙ БИБЛИОТЕКИ)
function checkAndCreateDestinationPath (fileDestination) {
const dirPath = fileDestination.split('/');
dirPath.forEach((element, index) => {
if(!fs.existsSync(dirPath.slice(0, index + 1).join('/'))){
fs.mkdirSync(dirPath.slice(0, index + 1).join('/'));
}
});
}
Ответ 14
Асинхронный способ рекурсивно создавать каталоги:
import fs from 'fs'
const mkdirRecursive = function(path, callback) {
let controlledPaths = []
let paths = path.split(
'/' // Put each path in an array
).filter(
p => p != '.' // Skip root path indicator (.)
).reduce((memo, item) => {
// Previous item prepended to each item so we preserve realpaths
const prevItem = memo.length > 0 ? memo.join('/').replace(/\.\//g, '')+'/' : ''
controlledPaths.push('./'+prevItem+item)
return [...memo, './'+prevItem+item]
}, []).map(dir => {
fs.mkdir(dir, err => {
if (err && err.code != 'EEXIST') throw err
// Delete created directory (or skipped) from controlledPath
controlledPaths.splice(controlledPaths.indexOf(dir), 1)
if (controlledPaths.length === 0) {
return callback()
}
})
})
}
// Usage
mkdirRecursive('./photos/recent', () => {
console.log('Directories created succesfully!')
})
Ответ 15
Здесь моя императивная версия mkdirp
для nodejs.
function mkdirSyncP(location) {
let normalizedPath = path.normalize(location);
let parsedPathObj = path.parse(normalizedPath);
let curDir = parsedPathObj.root;
let folders = parsedPathObj.dir.split(path.sep);
folders.push(parsedPathObj.base);
for(let part of folders) {
curDir = path.join(curDir, part);
if (!fs.existsSync(curDir)) {
fs.mkdirSync(curDir);
}
}
}
Ответ 16
Как насчет этого подхода:
if (!fs.existsSync(pathToFile)) {
var dirName = "";
var filePathSplit = pathToFile.split('/');
for (var index = 0; index < filePathSplit.length; index++) {
dirName += filePathSplit[index]+'/';
if (!fs.existsSync(dirName))
fs.mkdirSync(dirName);
}
}
Это работает для относительного пути.
Ответ 17
Основываясь на ответах нулевых зависимостей mouneer, здесь немного более удобный для начинающих вариант Typescript
в качестве модуля:
import * as fs from 'fs';
import * as path from 'path';
/**
* Recursively creates directories until 'targetDir' is valid.
* @param targetDir target directory path to be created recursively.
* @param isRelative is the provided 'targetDir' a relative path?
*/
export function mkdirRecursiveSync(targetDir: string, isRelative = false) {
const sep = path.sep;
const initDir = path.isAbsolute(targetDir) ? sep : '';
const baseDir = isRelative ? __dirname : '.';
targetDir.split(sep).reduce((prevDirPath, dirToCreate) => {
const curDirPathToCreate = path.resolve(baseDir, prevDirPath, dirToCreate);
try {
fs.mkdirSync(curDirPathToCreate);
} catch (err) {
if (err.code !== 'EEXIST') {
throw err;
}
// caught EEXIST error if curDirPathToCreate already existed (not a problem for us).
}
return curDirPathToCreate; // becomes prevDirPath on next call to reduce
}, initDir);
}
Ответ 18
Так чисто, как это :)
function makedir(fullpath) {
let destination_split = fullpath.replace('/', '\\').split('\\')
let path_builder = destination_split[0]
$.each(destination_split, function (i, path_segment) {
if (i < 1) return true
path_builder += '\\' + path_segment
if (!fs.existsSync(path_builder)) {
fs.mkdirSync(path_builder)
}
})
}
Ответ 19
Exec может быть запутанным для окон. Существует более "нудистское" решение. По сути, у вас есть рекурсивный вызов, чтобы увидеть, существует ли каталог и погрузиться в дочерний элемент (если он существует) или создать его. Вот функция, которая создаст дочерние элементы и вызовет функцию по окончании:
fs = require('fs');
makedirs = function(path, func) {
var pth = path.replace(/['\\]+/g, '/');
var els = pth.split('/');
var all = "";
(function insertOne() {
var el = els.splice(0, 1)[0];
if (!fs.existsSync(all + el)) {
fs.mkdirSync(all + el);
}
all += el + "/";
if (els.length == 0) {
func();
} else {
insertOne();
}
})();
}
Ответ 20
Эта версия работает лучше в Windows, чем в верхнем ответе, потому что она понимает как /
и path.sep
так и работу с косой чертой в Windows. Поддерживает абсолютные и относительные пути (относительно process.cwd
).
/**
* Creates a folder and if necessary, parent folders also. Returns true
* if any folders were created. Understands both '/' and path.sep as
* path separators. Doesn't try to create folders that already exist,
* which could cause a permissions error. Gracefully handles the race
* condition if two processes are creating a folder. Throws on error.
* @param targetDir Name of folder to create
*/
export function mkdirSyncRecursive(targetDir) {
if (!fs.existsSync(targetDir)) {
for (var i = targetDir.length-2; i >= 0; i--) {
if (targetDir.charAt(i) == '/' || targetDir.charAt(i) == path.sep) {
mkdirSyncRecursive(targetDir.slice(0, i));
break;
}
}
try {
fs.mkdirSync(targetDir);
return true;
} catch (err) {
if (err.code !== 'EEXIST') throw err;
}
}
return false;
}
Ответ 21
использование модуля mkdir-p
- хороший вариант, как некоторые из ответов. Но вместо этого я использовал js-функцию для своего приложения. Он работает для меня в среде Windows; не тестировались в mac/linux. Если вы уже используете jquery, тогда это будет удобно, я думаю. Или вы можете использовать эту логику с помощью чистого javascript.
var arrPath=loc.split(path.sep);
var tmpPath="";
$.each(arrPath,(index,value)=>{
if(index==0 && value==""){
/*in mac path starts with path seperator 'forward slash'
then after the split 0th element will be empty string*/
tmpPath=path.sep;
}
else{
if(index==0){
tmpPath=value;
}
else{
tmpPath=path.join(tmpPath,value);
}
if (!fs.existsSync(tmpPath)) { /* if the directory not exists*/
fs.mkdirSync(tmpPath);
}
}
});
if(fs.existsSync(tmpPath))
return true;
else
return false