如果使用Node.js不存在该如何创建目录?


650

如果目录不存在,这是创建目录的正确方法吗?它应该对该脚本具有完全的权限,并且可以被其他人读取。

var dir = __dirname + '/upload';
if (!path.existsSync(dir)) {
    fs.mkdirSync(dir, 0744);
}

Answers:


1271
var fs = require('fs');
var dir = './tmp';

if (!fs.existsSync(dir)){
    fs.mkdirSync(dir);
}

28
如果您要在应用启动或初始化时执行此操作,则可以阻止执行,因为如果要异步执行相同的操作,则可以阻止执行。如果将目录作为重复操作进行,则这是一种不好的做法,但可能不会引起任何性能问题,但是它仍然是一个坏习惯。仅用于启动您的应用程序,否则仅用于一次操作。
tsturzl 2015年

20
不建议使用existSync(),但不建议使用exist()- nodejs.org/api/fs.html#fs_fs_existssync_path
Ian Chadwick

使用* Sync方法通常是
禁忌

14
使用同步方法最适合本地脚本,对于服务器来说显然不是一个好主意。
码头

如果该块被setTimeout包围,那么它是异步的............
Bryan Grace

185

不,出于多种原因。

  1. path模块没有exists/ existsSync方法。它在fs模块中。(也许您刚刚在输入问题时打错了字?)

  2. 该文档明确劝阻您使用exists

    fs.exists()是过时的,仅出于历史原因存在。几乎永远没有理由在自己的代码中使用它。

    特别是,如果检查文件打开之前就存在它是一个反模式是让你容易受到竞争条件:另一个进程可能会删除调用的文件fs.exists()fs.open()。只需打开文件并在不存在时处理错误。

    由于我们在谈论目录而不是文件,因此此建议意味着您应该无条件调用mkdir和忽略EEXIST

  3. 通常,应避免使用* Sync方法。它们正在阻塞,这意味着当您转到磁盘时,程序中绝对不会发生任何其他事情。这是一个非常昂贵的操作,花费的时间打破了节点事件循环的核心假设。

    * Sync方法通常在单一用途的快速脚本(可以先执行一件事然后退出的脚本)中很好用,但是在编写服务器时几乎永远不要使用:您的服务器将在整个持续时间内无法响应任何人I / O请求。如果多个客户端请求需要I / O操作,则服务器将很快停止运行。


    我唯一考虑Sync在服务器应用程序中使用* 方法的操作是在启动时发生一次(只有一次)的操作。例如,require 实际上用于readFileSync加载模块。

    即使这样,您仍然必须小心,因为许多同步I / O可能会不必要地减慢服务器的启动时间。


    相反,您应该使用异步I / O方法。

因此,如果我们将这些建议汇总在一起,我们将得到如下结果:

function ensureExists(path, mask, cb) {
    if (typeof mask == 'function') { // allow the `mask` parameter to be optional
        cb = mask;
        mask = 0777;
    }
    fs.mkdir(path, mask, function(err) {
        if (err) {
            if (err.code == 'EEXIST') cb(null); // ignore the error if the folder already exists
            else cb(err); // something else went wrong
        } else cb(null); // successfully created folder
    });
}

我们可以这样使用它:

ensureExists(__dirname + '/upload', 0744, function(err) {
    if (err) // handle folder creation error
    else // we're all good
});

当然,这并不能解决诸如

  • 如果在程序运行时删除文件夹,会发生什么情况?(假设您在启动过程中仅检查一次它是否存在)
  • 如果该文件夹已存在但权限错误,会发生什么情况?

1
有没有一种方法可以避免SyntaxError:在严格模式下不允许使用八进制文字?
Whisher 2014年

8
将其写为小数。0744 == 484
josh3736 2014年

3
一种替代方法是使用扩展fs的模块以具有此功能,例如github.com/jprichardson/node-fs-extra
Bret

这个“ mask”标志在2019年仍然有意义吗?目的是什么?
老男孩

这是unix文件模式 -目录的读/写权限。
josh3736


33

mkdir方法具有递归创建的能力在路径中任何目录,而忽略存在的目录。

Node v10 / 11 docs中

// Creates /tmp/a/apple, regardless of whether `/tmp` and /tmp/a exist.
fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => {
    if (err) throw err;
});

注意:您需要导入内置 fs模块。

现在,这是一个更强大的示例,该示例利用本机ES模块(启用了标志并具有.mjs扩展名),处理了非根路径,并说明了完整的路径名:

import fs from 'fs';
import path from 'path';

createDirectories(pathname) {
   const __dirname = path.resolve();
   pathname = pathname.replace(/^\.*\/|\/?[^\/]+\.[a-z]+|\/$/g, ''); // Remove leading directory markers, and remove ending /file-name.extension
   fs.mkdir(path.resolve(__dirname, pathname), { recursive: true }, e => {
       if (e) {
           console.error(e);
       } else {
           console.log('Success');
       }
    });
}

您可以像这样使用它createDirectories('/components/widget/widget.js');

当然,您可能希望通过将诺言与async / await结合使用来在创建目录时以更具可读性的同步外观方式利用文件创建来获得更多的希望;但是,这超出了问题的范围。


1
为什么const __dirname = path.resolve(); 而不使用内置的__dirname?
TamusJRoyce '19

29

以防万一有人对单行版本感兴趣。:)

//or in typescript: import * as fs from 'fs';
const fs = require('fs');
!fs.existsSync(dir) && fs.mkdirSync(dir);

所谓的1班轮实际上不是1行。
混合网络开发人员

20

mkdir如果文件夹存在,则可以使用并捕获错误。
这是异步的(因此是最佳做法)且安全。

fs.mkdir('/path', err => { 
    if (err && err.code != 'EEXIST') throw 'up'
    .. safely do your stuff here  
    })

(可选地,在模式中添加第二个参数。)


其他想法:

  1. 您可以使用then或通过使用本地promisify等待。

    const util = require('util'), fs = require('fs');
    const mkdir = util.promisify(fs.mkdir);
    var myFunc = () => { ..do something.. } 
    
    mkdir('/path')
        .then(myFunc)
        .catch(err => { if (err.code != 'EEXIST') throw err; myFunc() })
  2. 您可以制作自己的promise方法,例如(未测试):

    let mkdirAsync = (path, mode) => new Promise(
       (resolve, reject) => mkdir (path, mode, 
          err => (err && err.code !== 'EEXIST') ? reject(err) : resolve()
          )
       )
  3. 对于同步检查,可以使用:

    fs.existsSync(path) || fs.mkdirSync(path)
  4. 或者您可以使用图书馆,其中最受欢迎的两个是

    • mkdirp(文件夹也一样)
    • fsextra(超集fs,添加了很多有用的东西)

1
对于有前途的方法#1,您可以重新安排渔获。 mkdir('/path').catch(err => { if (err.code != 'EEXIST') throw err;}).then(myFunc);
什么会很酷

!==不是使用!=
昆汀·罗伊

18

随着FS-额外的包,你可以做到这一点一行代码

const fs = require('fs-extra');

const dir = '/tmp/this/path/does/not/exist';
fs.ensureDirSync(dir);

如此低估的答案!fs-extra对我来说必不可少。我认为写10条以上行来检查文件夹是否存在是一种错误...
538ROMEO

10

最好的解决方案是使用名为node-fs-extra的npm模块。它有一个称为的方法mkdir,可以创建您提到的目录。如果提供长目录路径,它将自动创建父文件夹。该模块是npm模块的超集fs,因此,fs如果添加此模块,则也可以使用其中的所有功能。


6
var dir = 'path/to/dir';
try {
  fs.mkdirSync(dir);
} catch(e) {
  if (e.code != 'EEXIST') throw e;
}

4
对于Node.js v7.4.0,该文档指出fs.exists()已弃用,但fs.existsSync()没有。您是否可以添加指向fs.existsSync()已折旧的资源的链接?
弗朗西斯

1
仅代码的答案对以后遇到此问题的用户不是很有帮助。请编辑您的答案以解释您的代码为何能解决原始问题
yivi

3
@francis,嗯,我正在查看Node.js v5,nodejs.org
docs /

1
谢谢!似乎该函数存在于版本0.12中,在版本4和5中已弃用,并在版本6和7中得到恢复...一种僵尸函数...
弗朗西斯

1
是的,显然,从以下日期起,现在推荐使用它Apr 2018nodejs.org/api/fs.html#fs_fs_existssync_path
LeOn-Han Li

5
    var filessystem = require('fs');
    var dir = './path/subpath/';

    if (!filessystem.existsSync(dir)){
        filessystem.mkdirSync(dir);
    }else
    {
        console.log("Directory already exist");
    }

这可能对您有帮助:)


5

ENOENT:没有这样的文件或目录

const fs = require('fs')  // in javascript
import * as fs from "fs"  // in typescript
import fs from "fs"       // in typescript

// it will create the directory if it does not exist.
!fs.existsSync(`./assets/`) && fs.mkdirSync(`./assets/`, { recursive: true })

1
这正在工作,谢谢
Aljohn Yamaro

3

我想添加josh3736的答案的Typescript Promise重构

它做同样的事情,并具有相同的边缘情况,只是碰巧使用Promises,typescript typedefs并与“ use strict”一起使用。

// https://en.wikipedia.org/wiki/File_system_permissions#Numeric_notation
const allRWEPermissions = parseInt("0777", 8);

function ensureFilePathExists(path: string, mask: number = allRWEPermissions): Promise<void> {
    return new Promise<void>(
        function(resolve: (value?: void | PromiseLike<void>) => void,
            reject: (reason?: any) => void): void{
            mkdir(path, mask, function(err: NodeJS.ErrnoException): void {
                if (err) {
                    if (err.code === "EEXIST") {
                        resolve(null); // ignore the error if the folder already exists
                    } else {
                        reject(err); // something else went wrong
                    }
                } else {
                    resolve(null); // successfully created folder
                }
            });
    });
}

3

使用节点10 + ES6:

import path from 'path';
import fs from 'fs';

(async () => {
  const dir = path.join(__dirname, 'upload');

  try {
    await fs.promises.mkdir(dir);
  } catch (error) {
    if (error.code === 'EEXIST') {
      // Something already exists, but is it a file or directory?
      const lstat = await fs.promises.lstat(dir);

      if (!lstat.isDirectory()) {
        throw error;
      }
    } else {
      throw error;
    }
  }
})();

2

您可以使用节点文件系统命令fs.stat来检查dir是否存在,使用fs.mkdir来创建带有回调的目录,或者使用fs.mkdirSync来创建没有回调的目录,例如以下示例:

//first require fs
const fs = require('fs');

// Create directory if not exist (function)
const createDir = (path) => {
    // check if dir exist
    fs.stat(path, (err, stats) => {
        if (stats.isDirectory()) {
            // do nothing
        } else {
            // if the given path is not a directory, create a directory
            fs.mkdirSync(path);
        }
    });
};

1

这是一个递归创建目录的小功能:

const createDir = (dir) => {
  // This will create a dir given a path such as './folder/subfolder' 
  const splitPath = dir.split('/');
  splitPath.reduce((path, subPath) => {
    let currentPath;
    if(subPath != '.'){
      currentPath = path + '/' + subPath;
      if (!fs.existsSync(currentPath)){
        fs.mkdirSync(currentPath);
      }
    }
    else{
      currentPath = subPath;
    }
    return currentPath
  }, '')
}

0

使用异步/等待:

const mkdirP = async (directory) => {
  try {
    return await fs.mkdirAsync(directory);
  } catch (error) {
    if (error.code != 'EEXIST') {
      throw e;
    }
  }
};

您需要承诺fs

import nodeFs from 'fs';
import bluebird from 'bluebird';

const fs = bluebird.promisifyAll(nodeFs);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.