Как создать полный путь с узла ПС.mkdirSync?
Я пытаюсь создать полный путь, если он не существует.
код выглядит так:
var fs = require('fs');
if (!fs.existsSync(newDest)) fs.mkdirSync(newDest);
этот код отлично работает до тех пор, пока есть только один подкаталог (новый, как 'dir1') однако, когда есть путь к каталогу, как ('dir1/dir2') он терпит неудачу с
ошибка: ENOENT, нет такого файла или каталога
Я хотел бы иметь возможность создать полный путь с таким количеством строк кода, как это необходимо.
Я читал, что есть рекурсивный вариант на fs и попробовал его так
var fs = require('fs');
if (!fs.existsSync(newDest)) fs.mkdirSync(newDest,'0777', true);
Я чувствую, что это должно быть так просто, чтобы рекурсивно создать каталог, который не существует. Я что-то пропустил или мне нужно парсить путь и проверить каждый каталог и создать его, если он еще не существует?
Я довольно новичок в узле. Может быть, я использую старую версию FS?
13 ответов:
один из вариантов-использовать модуль shelljs
npm install shelljs
var shell = require('shelljs'); shell.mkdir('-p', fullPath);страницы:
возможные варианты:
p: полный путь (при необходимости будет создан промежуточный dirs)
Как отмечали другие, есть и другие более сфокусированные модули. Но, за пределами mkdirp, у него есть тонны других полезных операций оболочки (например, grep и т. д...) и он работает на windows и *nix
создать каталоги рекурсивно, если они не существуют! (ноль зависимостей)
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 && targetDir === curDir) { throw err; // Throw if it's 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 comment.
- в случае относительных путей целевые каталоги будут созданы (разрешены) в текущем рабочем каталоге. Чтобы разрешить их относительно текущего скрипта dir, передайте
{isRelativeToScript: true}.- используя
path.sepиpath.resolve()не только/конкатенация, чтобы избежать кросс-платформенные проблемы.- используя
fs.mkdirSyncи обработка ошибки сtry/catchесли брошен для обработки условий гонки: другой процесс может добавить файл между вызовамиfs.existsSync()иfs.mkdirSync()и вызывает исключение.
- другим способом достижения этого может быть проверка наличия файла, а затем его создание, т. е.
if (!fs.existsSync(curDir) fs.mkdirSync(curDir);. Но это анти-шаблон, который оставляет код уязвимым для условий гонки. Благодаря @GershomMaes комментарий о проверке существования каталога.- требует узел v6 и новее для поддержки деструктурируется. (Если у вас есть проблемы с реализацией этого решения со старыми версиями узлов, просто оставьте мне комментарий)
более надежный ответ-использовать use 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'....
fs-extra добавляет методы файловой системы, которые не включены в собственный модуль fs. Это капля в замене для fs.
установить
fs-extra
$ npm install --save fs-extravar 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
С помощью reduce мы можем проверить, существует ли каждый путь и создать его при необходимости, также таким образом, я думаю, что легче следовать. Отредактировано, спасибо @Arvin, мы должны использовать путь.sep, чтобы получить правильный разделитель сегментов пути для конкретной платформы.
const path = require('path'); // Path separators could change depending on the platform const pathToCreate = 'path/to/dir'; pathToCreate .split(path.sep) .reduce((currentPath, folder) => { currentPath += folder + path.sep; if (!fs.existsSync(currentPath)){ fs.mkdirSync(currentPath); } return currentPath; }, '');
асинхронный способ рекурсивного создания каталогов:
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!') })
Как насчет такого подхода :
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); } }это работает для относительного пути.
слишком много ответов, но вот решение без рекурсии, которое работает, разделяя путь, а затем слева направо, создавая его снова
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 против Linux, просто замените прямую косую черту на двойную обратную косую черту ' \ ' в обоих случаях выше, но TBH мы говорим о node fs не командной строке windows, и первый довольно Прощающий, и приведенный выше код будет просто работать на Windows и является более полным решением межплатформенный.
вот моя императивная версия
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); } } }
на основе Мунир это ответ с нулевыми зависимостями, вот немного более дружелюбный новичок
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); }
Exec может быть грязным на windows. Существует более "Ноди" решение. По сути, у вас есть рекурсивный вызов, чтобы увидеть, существует ли каталог, и погрузиться в дочерний элемент (если он существует) или создать его. Вот функция, которая будет создавать детей и вызывать функцию, когда закончите:
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(); } })();}
эта версия работает лучше на Windows, чем верхний ответ, потому что он понимает как
/иpath.sepТак что вперед косые черты работают на окнах, как они должны. Поддерживаются абсолютные и относительные пути (относительно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; }
С помощью модуля
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
Comments