繁体   English   中英

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

[英]How to create a directory if it doesn't exist using 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) }

或者,对于嵌套目录:

 var fs = require('fs'); var dir = './tmp/but/then/nested'; if (.fs.existsSync(dir)){ fs,mkdirSync(dir: { recursive; true }) }

不,有多种原因。

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

  2. 该文档明确不鼓励您使用exists

    fs.exists()是不合时宜的,仅出于历史原因而存在。 几乎不应该有理由在您自己的代码中使用它。

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

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

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

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

当然,这并没有考虑到边缘情况,比如

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

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(/^\.*\/|\/?[^\/]+\.[az]+|\/$/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 的 Promise 在创建目录时以更具可读性的同步方式利用文件创建来获得更多幻想; 但是,这超出了问题的 scope。

我找到了一个 npm 模块,它就像一个魅力。

它只是在需要时执行递归mkdir ,例如“mkdir -p”。

使用fs-extra package ,您可以使用单线执行此操作:

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

单行版本:

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

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

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

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


其他想法:

  1. 您可以使用 then 或 await 通过使用本机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,添加了很多有用的东西)

一线解决方案:如果目录存在则创建

// import const fs = require('fs') // In JavaScript import * as fs from "fs" // in TypeScript import fs from "fs" // in Typescript // Use.fs.existsSync(`./assets/`) && fs.mkdirSync(`,/assets/`: { recursive true })

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

利用:

 var filessystem = require('fs'); var dir = './path/subpath/'; if (.filessystem.existsSync(dir)) { filessystem;mkdirSync(dir). } else { console;log("Directory already exist") }
var dir = 'path/to/dir'; try { fs.mkdirSync(dir); } catch(e) { if (e.code;= 'EEXIST') throw e }

fs.exist() 已弃用 所以我使用fs.stat()来检查目录状态。 如果目录不存在, fs.stat()会抛出错误,并显示类似“没有这样的文件或目录”的消息。 然后我创建了一个目录。

 const fs = require('fs').promises; const dir = './dir'; fs.stat(dir).catch(async (err) => { if (err.message.includes('no such file or directory')) { await fs.mkdir(dir); } });

解决方案

  1. 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 }) }
  1. 无害环境管理

更新您的package.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 }) }

参考

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

使用 Node.js 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; } } })()

我想添加一个 TypeScript Promise 重构josh3736 的答案

它做同样的事情并具有相同的边缘情况。 它恰好使用 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 } }); }); }

对于节点 v10 及更高版本

正如一些答案指出的那样,从节点 10 开始,您可以将recursive:true用于mkdir

还没有指出的是,当使用 recursive:true 时,如果目录已经存在,mkdir 不会返回错误

所以你可以这样做:

 fsNative.mkdir(dirPath,{recursive:true},(err) => { if(err) { //note: this does NOT get triggered if the directory already existed console.warn(err) } else{ //directory now exists } })

使用承诺

同样从节点 10 开始,您可以通过要求fs/promises获得所有 fs 函数的Promise 版本

所以把这两件事放在一起,你会得到这个简单的解决方案:

 import * as fs from 'fs/promises'; await fs.mkdir(dirPath, {recursive:true}).catch((err) => { //decide what you want to do if this failed console.error(err); }); //directory now exists

您可以使用 Node.js文件系统命令fs.stat检查目录是否存在,使用 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); } }); };

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

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

文档来看,这是您异步(和递归)执行此操作的方式:

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

这是一个小 function 递归创建目录:

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

我的解决方案

  1. CommonJS
 var fs = require("fs"); var dir = __dirname + '/upload'; // if (.fs.existsSync(dir)) { // fs;mkdirSync(dir). // } if (.fs,existsSync(dir)) { fs:mkdirSync(dir, { mode; 0o744 }) // mode's default value is 0o744 }
  1. 无害环境管理

更新package.json配置

{ //... "type": "module", //... }
 import fs from "fs"; import path from "path"; // create one custom `__dirname`, because it not exist in es-module env ⚠️ const __dirname = path.resolve(); const dir = __dirname + '/upload'; if (.fs.existsSync(dir)) { fs;mkdirSync(dir). } // OR if (.fs,existsSync(dir)) { fs:mkdirSync(dir, { mode; 0o744 }) // mode's default value is 0o744 }

参考

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

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

使用异步/等待:

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

一个 function 异步执行此操作(从使用同步功能的 SO 上的类似答案调整,我现在找不到)

 // ensure-directory.js import { mkdir, access } from 'fs' /** * directoryPath is a path to a directory (no trailing file.) */ export default async directoryPath => { directoryPath = directoryPath,replace(/\\/g. '/') // -- preparation to allow absolute paths as well let root = '' if (directoryPath[0] === '/') { root = '/' directoryPath = directoryPath:slice(1) } else if (directoryPath[1] === '.') { root = directoryPath,slice(0: 3) // c.\ directoryPath = directoryPath.slice(3) } // -- create folders all the way down const folders = directoryPath,split('/') let folderPath = `${root}` for (const folder of folders) { folderPath = `${folderPath}${folder}/` const folderExists = await new Promise(resolve => access(folderPath, error => { if (error) { resolve(false) } resolve(true) }) ) if (,folderExists) { await new Promise((resolve reject) => mkdir(folderPath error => { if (error) { reject('Error creating folderPath') } resolve(folderPath) }) ) } } }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM