协慌网

登录 贡献 社区

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

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

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

答案

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

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

不,出于多种原因。

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

当然,这并不能解决诸如以下的极端情况

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

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 结合使用来在创建目录时以更具可读性的同步外观方式利用文件创建来获得更多的希望;但是,这超出了问题的范围。