如何使用fs.mkdirSync创建全路径?

我试图创建一个完整的路径,如果它不存在。

代码如下所示:

var fs = require('fs');
if (!fs.existsSync(newDest)) fs.mkdirSync(newDest);
只要只有一个子目录(像'dir1'这样的newDest),这段代码就能很好地工作,但是当有('dir1/dir2')这样的目录路径时,它就会失败 错误:ENOENT,没有这样的文件或目录

我希望能够用尽可能少的代码行创建完整的路径。

我读到fs上有一个递归选项,并尝试了这样做

var fs = require('fs');
if (!fs.existsSync(newDest)) fs.mkdirSync(newDest,'0777', true);

我觉得递归地创建一个不存在的目录应该这么简单。我是否遗漏了一些东西,或者我是否需要解析路径并检查每个目录,如果它不存在,则创建它?

我对Node很陌生。也许我使用的是旧版本的FS?

214402 次浏览

一个选项是使用shelljs模块

NPM安装shelljs

var shell = require('shelljs');
shell.mkdir('-p', fullPath);

从那一页开始:

可用的选项:

P:全路径(如有必要将创建中间dirs)

正如其他人所注意到的,还有其他更专注的模块。但是,除了mkdirp之外,它还有大量其他有用的shell操作(比如which, grep等等…),并且它可以在windows和*nix上工作

编辑:评论建议这在没有mkdir cli实例的系统上不起作用。事实并非如此。这就是shelljs的重点——创建一个可移植的跨平台shell类函数集。它适用于偶数窗户。

一个更可靠的答案是使用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'....

更新

NodeJS版本10.12.0增加了对mkdirmkdirSync的本地支持,可以递归地创建带有recursive: true选项的目录,如下所示:

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'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');

演示

试试!< /强> < / >

解释

使用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((prevPath, folder) => {
const currentPath = path.join(prevPath, folder, path.sep);
if (!fs.existsSync(currentPath)){
fs.mkdirSync(currentPath);
}
return currentPath;
}, '');

fs-extra添加了本地fs模块中不包含的文件系统方法。它是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

递归创建目录的异步方法:

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!')
})

下面是nodejs的mkdirp命令式版本。

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);
}
}
}

这个方法怎么样:

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);
}
}

这适用于相对路径。

基于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);
}

Exec在windows上可能很乱。还有一个更“nodie”的解决方案。基本上,您有一个递归调用来查看目录是否存在,并深入子目录(如果存在)或创建它。下面是一个函数,它将创建子函数,并在完成时调用一个函数:

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();
}
})();

答案太多了,但这里有一个没有递归的解决方案,它通过分割路径,然后从左到右重新构建

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,我们谈论的是节点fs而不是windows命令行,前者是相当宽容的,上面的代码将简单地工作在windows上,是一个更完整的跨平台解决方案。

这个版本在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;
}
此特性已在10.12.0版本中添加到node.js中,因此只需将选项{recursive: true}作为fs.mkdir()调用的第二个参数传递给它即可。 参见官方文档中的例子.

.

不需要外部模块或自己的实现。

我知道这是一个老问题,但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;
});
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);
}

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

您可以使用下一个函数

const recursiveUpload = (path: string) => { Const paths = path.split("/")

const fullPath = paths.reduce((accumulator, current) => {
fs.mkdirSync(accumulator)
return `${accumulator}/${current}`
})


fs.mkdirSync(fullPath)


return fullPath
}

它的作用是:

  1. 创建paths变量,它将每个路径单独存储为数组的一个元素。
  2. 在数组中每个元素的末尾添加"/"。
  3. 构成循环:
    1. 从索引从0到当前迭代的数组元素的连接中创建一个目录。基本上,它是递归的。
    2. 李< / ol > < / >

    希望有帮助!

    顺便说一下,在Node v10.12.0中,可以通过将递归路径创建作为附加参数来使用。

    < p > <代码> fs。Mkdir ('/tmp/a/apple', {recursive: true}, (err) =>{ 如果(错误)抛出错误; }); < /代码> < / p >

    https://nodejs.org/api/fs.html#fs_fs_mkdirsync_path_options

你可以简单地检查文件夹存在或不递归的路径,并使文件夹,因为你检查,如果他们不存在。(没有外部库)

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('/'));
}
});
}

像这样干净:)

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)
}
})
}

现在NodeJS >= 10.12.0,你可以使用fs.mkdirSync(path, { recursive: true }) fs.mkdirSync

我对fs的递归选项有问题。mkdir,所以我做了一个函数,做以下工作:

  1. 创建所有目录的列表,从最终目标目录开始,一直到根父目录。
  2. 为mkdir函数创建一个所需目录的新列表
  3. 使所需的每个目录,包括final目录

    function createDirectoryIfNotExistsRecursive(dirname) {
    return new Promise((resolve, reject) => {
    const fs = require('fs');
    
    
    var slash = '/';
    
    
    // backward slashes for windows
    if(require('os').platform() === 'win32') {
    slash = '\\';
    }
    // initialize directories with final directory
    var directories_backwards = [dirname];
    var minimize_dir = dirname;
    while (minimize_dir = minimize_dir.substring(0, minimize_dir.lastIndexOf(slash))) {
    directories_backwards.push(minimize_dir);
    }
    
    
    var directories_needed = [];
    
    
    //stop on first directory found
    for(const d in directories_backwards) {
    if(!(fs.existsSync(directories_backwards[d]))) {
    directories_needed.push(directories_backwards[d]);
    } else {
    break;
    }
    }
    
    
    //no directories missing
    if(!directories_needed.length) {
    return resolve();
    }
    
    
    // make all directories in ascending order
    var directories_forwards = directories_needed.reverse();
    
    
    for(const d in directories_forwards) {
    fs.mkdirSync(directories_forwards[d]);
    }
    
    
    return resolve();
    });
    }
    

我用这种方法解决了这个问题——类似于其他递归的答案,但对我来说,这更容易理解和阅读。

const path = require('path');
const fs = require('fs');


function mkdirRecurse(inputPath) {
if (fs.existsSync(inputPath)) {
return;
}
const basePath = path.dirname(inputPath);
if (fs.existsSync(basePath)) {
fs.mkdirSync(inputPath);
}
mkdirRecurse(basePath);
}