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

代码如下所示:

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?


当前回答

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

Creates a list of all directories, starting with the final target dir and working up to the root parent. Creates a new list of needed directories for the mkdir function to work Makes each directory needed, including the 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(); }); }

其他回答

更新

NodeJS 10.12.0版本增加了对mkdir和mkdirSync的本地支持,使用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');

Demo

试一试!

解释

[UPDATE] This solution handles platform-specific errors like EISDIR for Mac and EPERM and EACCES for Windows. Thanks to all the reporting comments by @PediT., @JohnQ, @deed02392, @robyoder and @Almenon. This solution handles both relative and absolute paths. Thanks to @john comment. In the case of relative paths, target directories will be created (resolved) in the current working directory. To Resolve them relative to the current script dir, pass {isRelativeToScript: true}. Using path.sep and path.resolve(), not just / concatenation, to avoid cross-platform issues. Using fs.mkdirSync and handling the error with try/catch if thrown to handle race conditions: another process may add the file between the calls to fs.existsSync() and fs.mkdirSync() and causes an exception. The other way to achieve that could be checking if a file exists then creating it, I.e, if (!fs.existsSync(curDir) fs.mkdirSync(curDir);. But this is an anti-pattern that leaves the code vulnerable to race conditions. Thanks to @GershomMaes comment about the directory existence check. Requires Node v6 and newer to support destructuring. (If you have problems implementing this solution with old Node versions, just leave me a comment)

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

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

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

这适用于相对路径。

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

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