如果目录不存在,下面的方法是否正确?

它应该对脚本具有完全的权限,并且其他人可以阅读。

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

当前回答

如果子目录不存在,我必须创建子目录。我用了这个:

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

function ensureDirectoryExists(p) {
    //console.log(ensureDirectoryExists.name, {p});
    const d = path.dirname(p);
    if (d && d !== p) {
        ensureDirectoryExists(d);
    }
    if (!fs.existsSync(d)) {
        fs.mkdirSync(d);
    }
}

其他回答

最好的解决方案是使用名为node-fs-extra的npm模块。它有一个名为mkdir的方法,用于创建您提到的目录。如果您给出一个较长的目录路径,它将自动创建父文件夹。这个模块是npm模块fs的超集,所以如果你添加了这个模块,你也可以使用fs中的所有函数。

从文档中可以看出,这是你异步(和递归)做这件事的方式:

const fs = require('fs');
const fsPromises = fs.promises;

fsPromises.access(dir, fs.constants.F_OK)
   .catch(async() => {
                await fs.mkdir(dir, { recursive: true }, function(err) {
                    if (err) {
                      console.log(err)
                    }
                  })
    });

mkdir方法能够递归地在一个路径中创建任何不存在的目录,并忽略存在的目录。

来自Node.js v10/11文档:

// 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模块。

现在,这里有一个更健壮的例子,它利用了原生ECMAScript模块(启用了标志和.mjs扩展名),处理非根路径,并说明完整的路径名:

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

function 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中使用promises以一种更可读的同步方式来利用文件创建;但是,这超出了问题的范围。

不,原因有很多。

The path module does not have an exists/existsSync method. It is in the fs module. (Perhaps you just made a typo in your question?) The documentation explicitly discourage you from using exists. fs.exists() is an anachronism and exists only for historical reasons. There should almost never be a reason to use it in your own code. In particular, checking if a file exists before opening it is an anti-pattern that leaves you vulnerable to race conditions: another process may remove the file between the calls to fs.exists() and fs.open(). Just open the file and handle the error when it's not there. Since we're talking about a directory rather than a file, this advice implies you should just unconditionally call mkdir and ignore EEXIST. In general, you should avoid the *Sync methods. They're blocking, which means absolutely nothing else in your program can happen while you go to the disk. This is a very expensive operation, and the time it takes breaks the core assumption of node's event loop. The *Sync methods are usually fine in single-purpose quick scripts (those that do one thing and then exit), but should almost never be used when you're writing a server: your server will be unable to respond to anyone for the entire duration of the I/O requests. If multiple client requests require I/O operations, your server will very quickly grind to a halt. The only time I'd consider using *Sync methods in a server application is in an operation that happens once (and only once), at startup. For example, require actually uses readFileSync to load modules. Even then, you still have to be careful because lots of synchronous I/O can unnecessarily slow down your server's startup time. Instead, you should use the asynchronous I/O methods.

所以如果我们把这些建议放在一起,我们会得到这样的结果:

function ensureExists(path, mask, cb) {
    if (typeof mask == 'function') { // Allow the `mask` parameter to be optional
        cb = mask;
        mask = 0o744;
    }
    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', 0o744, function(err) {
    if (err) // Handle folder creation error
    else // We're all good
});

当然,这并不能解释边缘情况,比如

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

解决方案

CommonJS

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

const dir = path.resolve(path.join(__dirname, 'upload');

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

// OR
if (!fs.existsSync(dir)) {
  fs.mkdirSync(dir, {
    mode: 0o744, // Not supported on Windows. Default: 0o777
  });
}

ESM

更新您的包。Json文件配置

{
  // declare using ECMAScript modules(ESM)
  "type": "module",
  //...
}
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

// create one custom `__dirname`, because it does not exist in es-module env ⚠️
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const dir = path.resolve(path.join(__dirname, 'upload');

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

// OR
if (!fs.existsSync(dir)) {
  fs.mkdirSync(dir, {
    mode: 0o744, // Not supported on Windows. Default: 0o777
  });
}

更新2022

import { existsSync } from 'node:fs';

refs

NodeJS版本:v18.2.0

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

https://nodejs.org/api/fs.html#fsmkdirsyncpath-options

https://nodejs.org/api/url.html#urlfileurltopathurl

https://github.com/nodejs/help/issues/2907#issuecomment-757446568

ESM: ECMAScript模块

https://nodejs.org/api/esm.html#introduction