Node.js创建文件夹或使用现有文件夹。

231
我已经阅读了 Node.js 的文档,除非我错过了什么,否则它没有说明某些操作中参数包含什么,特别是 fs.mkdir()。正如您在文档中所看到的那样,内容并不多。
目前,我有这段代码,试图创建一个文件夹或者使用一个现有的文件夹:
fs.mkdir(path,function(e){
    if(!e || (e && e.code === 'EEXIST')){
        //do something with contents
    } else {
        //debug
        console.log(e);
    }
});

但是我想知道这样做是否正确?检查代码EEXIST是知道文件夹已经存在的正确方法吗?我知道在创建目录之前可以使用fs.stat(),但这将对文件系统造成两次访问。

其次,是否有完整或至少更详细的Node.js文档,其中包含有关错误对象包含什么、参数表示什么等详细信息。


33
小小的建议,但请去掉e&&。如果!e失败,则知道e是真值。 - I Hate Lazy
15个回答

276

编辑: 由于这篇回答非常受欢迎,我已经更新了它以反映最新的做法。

Node >=10

Node的fs现在通过新选项{ recursive: true }可以本地支持递归创建目录。该选项模仿UNIX的mkdir -p行为,将确保路径中的每个部分都递归存在,并且如果其中任何一个不存在,则不会抛出错误。

(注意:它可能仍会抛出EPERMEACCESS等错误,因此最好在try {} catch (e) {}中封装它,如果您的实现容易受到这些错误的影响。)

同步版本。

fs.mkdirSync(dirpath, { recursive: true })

异步版本

await fs.promises.mkdir(dirpath, { recursive: true })

旧版本的Node

使用 try {} catch (err) {},可以非常优雅地实现此操作,而不会遇到竞争条件。

为了防止在检查目录是否存在和创建目录之间出现死时间,我们直接尝试创建它,如果出现 EEXIST 错误(目录已经存在),则忽略该错误。

但是,如果错误不是 EEXIST,那么我们应该抛出一个错误,因为可能涉及 EPERMEACCES 等问题。

function ensureDirSync (dirpath) {
  try {
    return fs.mkdirSync(dirpath)
  } catch (err) {
    if (err.code !== 'EEXIST') throw err
  }
}

若要实现类似mkdir -p的递归行为,例如./a/b/c,则您需要在目录路径中每个部分上调用它,例如./a./a/b./a/b/c


var fs = Npm.require('fs'); var dir = process.env.PWD + '/files/users/' + this.userId + '/';尝试 { fs.mkdirSync(dir); } catch (e) { 如果 (e.code != 'EEXIST') 抛出 e; } - Aaron
我尝试了你的代码,创建了一个使用目录创建的js脚本,像这样:mkdirpSync(path.join(__dirname, 'first', 'second', 'third', 'ololol', 'works'));但是出现了以下错误:$ node 1.js fs.js:747 return binding.mkdir(pathModule._makeLong(path), ^ Error: EPERM,操作不允许'C:' at Error (native) at Object.fs.mkdirSync (fs.js:747:18) at mkdirpSync (C:\Users\MAXIM\Desktop\test\1.js:15:8) at Object.<anonymous> (C:\Users\MAXIM\Desktop\test\1.js:19:1) ...请问可能出了什么问题?显然是在Windows上使用的 :) - Maksim Nesterenko
EPERM看起来像是一个权限问题,所以脚本无论如何都会中断执行。 - Christophe Marois
我认为这样会更好:var mkdirpSync = function (dirpath) { var parts = dirpath.split(path.sep); for( var i = 1; i <= parts.length; i++ ) { try { fs.mkdirSync( path.join.apply(null, parts.slice(0, i)) ); } catch (error) { if ( error.code != 'EEXIST' ){ throw error; } } } } - manish
2
警告:如果您的路径以 / 开头,则它不起作用。 - acemtp
已更新以反映最新的实践。 - Christophe Marois

244

一个很好的做法是使用mkdirp模块。

$ npm install mkdirp

使用它来运行需要该目录的函数。当路径被创建或者已经存在时,回调函数会被调用。如果mkdirp无法创建目录路径,则设置错误err

var mkdirp = require('mkdirp');
mkdirp('/tmp/some/path/foo', function(err) { 

    // path exists unless there was an error

});

6
我认为正确(即“不添加依赖项”)的答案是下面那个,由 @Raugaral 使用 fs.exists(Sync) 得出。 - Ricardo Pedroni
5
正确的方法是使用模块。模块通常全心全意地尝试解决一个问题,并经常得到维护。您可以很容易地通过npm更新它们。此外,您应该特别避免使用fs.exists[Sync],因为其使用会涉及竞态条件。 - 1j01
22
我不相信正确的方法是在本地平台已经支持该操作时使用一个模块。这将导致混乱。从技术角度来看,我必须同意有更好的答案。 - c..
3
同时,使用同步操作意味着存在竞态条件,因为它们的使用是对竞态条件的一种解决方案。 - c..
2
为什么不使用内部节点 fs 功能? - Seal
显示剩余8条评论

75

如果你想要一个快速又简单的一行代码,可以使用这个:

fs.existsSync("directory") || fs.mkdirSync("directory");

2
fs.exists 已经被弃用: https://nodejs.org/api/fs.html#fs_fs_exists_path_callback - adius
9
fs.existsSync(...)并未被弃用,因此这个回答看起来是可以的。 - Dan Haywood
注意!无法处理“dir/foo/bar”格式的路径,即缺少mkdir -p标志功能。 - Karl Pokus
这也存在竞态条件。 - Evert
1
通过@christophe-marois的递归标志,相当于POSIX mkdir -p标志,并且对于路径中缺少的文件夹也有效: fs.existsSync("dir/foo/bar") || fs.mkdirSync("dir/foo/bar", {recursive: true}) - andiOak

27

fs.mkdir的node.js文档基本上参考了Linux man页面mkdir(2)。这表明,如果该路径已存在但不是创建目录,则也会指示EEXIST,如果您选择此路线,则会出现尴尬的情况。

您最好调用fs.stat,它将在单个调用中告诉您路径是否存在以及它是否为目录。对于(我假设的)正常情况,即目录已经存在的情况下,只需要一个文件系统访问。

这些fs模块方法是本地C API的薄包装器,因此必须检查node.js文档中引用的man页面以获取详细信息。


20
在调用mkdir之前调用stat可能存在竞争条件,请谨记。 - Roger Lipscombe

26
你可以使用这个:
if(!fs.existsSync("directory")){
    fs.mkdirSync("directory", 0766, function(err){
        if(err){
            console.log(err);
            // echo the result back
            response.send("ERROR! Can't make the directory! \n");
        }
    });
}

1
我不相信这段代码会起作用,如果实体根本不存在,statSync 就会抛出错误,导致代码崩溃。你需要将其放在 try/catch 块中进行包装处理。 - Chris Foster
2
抱歉,我错了。将“statSync”更改为“existsSync”。 - Raugaral
5
根据http://nodejs.org/api/fs.html#fs_fs_mkdirsync_path_mode,mkdir的同步变体不接受回调函数。 - danwellman
1
根据 https://nodejs.org/api/fs.html#fs_fs_existssync_path,`fs.existsSync()` 和 fs.exists() 将被弃用。 - pau.moreno
错误。fs.exists()已经被弃用,因为回调参数不兼容。fs.existsSync()方法仍然可用。 - Amesys

9
我提出一种无需模块的解决方案(对于可在几行内编写的小型函数,特别是为了可维护性而不推荐使用累积模块...): 最新更新: 在v10.12.0中,NodeJS实现了递归选项:
// Create recursive folder
fs.mkdir('my/new/folder/create', { recursive: true }, (err) => { if (err) throw err; });

更新:

// Get modules node
const fs   = require('fs');
const path = require('path');

// Create 
function mkdirpath(dirPath)
{
    if(!fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK))
    {
        try
        {
            fs.mkdirSync(dirPath);
        }
        catch(e)
        {
            mkdirpath(path.dirname(dirPath));
            mkdirpath(dirPath);
        }
    }
}

// Create folder path
mkdirpath('my/new/folder/create');

fs.exists()在Node v9中已被弃用,请使用fs.access()代替。如果文件存在,则返回undefined;否则会抛出一个ENOENT错误。 - chharvey
没有使用任何npm包,它也能正常运行。这是一段很有价值的代码。谢谢。 - Karthik Sridharan
这个方法更好,可以在现有的长路径下创建文件夹。谢谢,伙计。 - Tsung Wu
1
fs.mkdirSync('my/new/folder/create', {recursive: true})?怎么样? - saitho
谢谢!我更新了我的帖子以帮助其他人。Node 10.12.0太新了。 - Liberateur
好的。谢谢。 - user2503775

4
你也可以使用fs-extra,它提供了很多常用的文件操作。

示例代码:

var fs = require('fs-extra')

fs.mkdirs('/tmp/some/long/path/that/prob/doesnt/exist', function (err) {
  if (err) return console.error(err)
  console.log("success!")
})

fs.mkdirsSync('/tmp/another/path')

文档在这里:https://github.com/jprichardson/node-fs-extra#mkdirsdir-callback

该文档是关于it技术中的node-fs-extra库中的mkdirsdir回调函数的说明。请点击链接查看详细信息。

4

以下是我用来创建目录(当它不存在时)的ES6代码:

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

function createDirectory(directoryPath) {
  const directory = path.normalize(directoryPath);

  return new Promise((resolve, reject) => {
    fs.stat(directory, (error) => {
      if (error) {
        if (error.code === 'ENOENT') {
          fs.mkdir(directory, (error) => {
            if (error) {
              reject(error);
            } else {
              resolve(directory);
            }
          });
        } else {
          reject(error);
        }
      } else {
        resolve(directory);
      }
    });
  });
}

const directoryPath = `${__dirname}/test`;

createDirectory(directoryPath).then((path) => {
  console.log(`Successfully created directory: '${path}'`);
}).catch((error) => {
  console.log(`Problem creating directory: ${error.message}`)
});

注意:

  • createDirectory函数的开头,我规范化了路径以确保操作系统的路径分隔符类型始终一致(例如,这将把C:\ directory / test 转换为C:\ directory \ test (当在Windows上时)
  • fs.exists已被弃用,因此我使用fs.stat来检查目录是否已存在
  • 如果目录不存在,则错误代码将是ENOENTError NO ENTry)
  • 目录本身将使用fs.mkdir创建
  • 我更喜欢异步函数fs.mkdir而不是其阻塞对应函数fs.mkdirSync,并且由于包装的Promise,它将保证仅在成功创建目录后才返回目录的路径

谢谢你提供了一个干净的解决方案,没有涉及不必要的模块。它对我非常有效。我希望还有更多像这样的答案! - Ken Lyon

3

在我看来,编写JavaScript代码时最好不要计算文件系统的访问次数。 不过,有两种方法可以做到你想要的效果:(1)使用statmkdir命令;(2)使用mkdir并检查(或丢弃)错误代码。


1
新建一个目录或使用已存在的目录是个好方法。我不明白为什么你不明白。检查错误代码是礼貌的好做法,而忽略错误代码只是好的做法。你不同意吗? - Chul-Woong Yang
1
我理解你的观点。然而,我认为有很多方法可以做得正确。谢谢。 - Chul-Woong Yang

2
为每个用户创建动态名称目录...使用此代码。
***suppose email contain user mail address***

var filessystem = require('fs');
var dir = './public/uploads/'+email;

if (!filessystem.existsSync(dir)){
  filessystem.mkdirSync(dir);

}else
{
    console.log("Directory already exist");
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接