简体   繁体   English

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

[英]How to create a directory if it doesn't exist using Node.js

Is the following the right way to create a directory if it doesn't exist?如果目录不存在,以下是创建目录的正确方法吗?

It should have full permission for the script and readable by others.它应该对脚本有完全的权限并且可以被其他人阅读。

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

For individual dirs:对于个人目录:

 var fs = require('fs'); var dir = './tmp'; if (.fs.existsSync(dir)){ fs;mkdirSync(dir) }

Or, for nested dirs:或者,对于嵌套目录:

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

No, for multiple reasons.不,有多种原因。

  1. The path module does not have an exists / existsSync method. path模块没有exists / existsSync方法。 It is in the fs module.它在fs模块中。 (Perhaps you just made a typo in your question?) (也许您只是在问题中打错了字?)

  2. The documentation explicitly discourage you from using exists .该文档明确不鼓励您使用exists

    fs.exists() is an anachronism and exists only for historical reasons. fs.exists()是不合时宜的,仅出于历史原因而存在。 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() .特别是,在打开文件之前检查文件是否存在是一种反模式,这会使您容易受到竞争条件的影响:另一个进程可能会在调用fs.exists()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 .由于我们谈论的是目录而不是文件,因此该建议意味着您应该无条件地调用mkdir并忽略EEXIST

  3. In general, you should avoid the * Sync methods.一般来说,您应该避免使用 * Sync方法。 They're blocking, which means absolutely nothing else in your program can happen while you go to the disk.它们正在阻塞,这意味着当您将 go 写入磁盘时,您的程序中绝对不会发生任何其他事情。 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. * Sync方法在单一用途的快速脚本中通常很好(那些做一件事然后退出的脚本),但在编写服务器时几乎不应该使用:您的服务器将无法响应任何人整个I/O 请求的持续时间。 If multiple client requests require I/O operations, your server will very quickly grind to a halt.如果多个客户端请求需要 I/O 操作,您的服务器将很快停止运行。


    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.我唯一一次考虑在服务器应用程序中使用 * Sync方法是在启动时发生一次(并且仅一次)的操作中。 For example, requireactually uses readFileSync to load modules.例如, require实际上使用readFileSync来加载模块。

    Even then, you still have to be careful because lots of synchronous I/O can unnecessarily slow down your server's startup time.即使这样,您仍然必须小心,因为大量同步 I/O 会不必要地减慢服务器的启动时间。


    Instead, you should use the asynchronous I/O methods.相反,您应该使用异步 I/O 方法。

So if we put together those pieces of advice, we get something like this:所以如果我们把这些建议放在一起,我们会得到这样的结果:

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

And we can use it like this:我们可以这样使用它:

 ensureExists(__dirname + '/upload', 0o744, function(err) { if (err) // Handle folder creation error else // We're all good });

Of course, this doesn't account for edge cases like当然,这并没有考虑到边缘情况,比如

  • What happens if the folder gets deleted while your program is running?如果在程序运行时文件夹被删除会怎样? (assuming you only check that it exists once during startup) (假设您只在启动期间检查它是否存在一次)
  • What happens if the folder already exists, but with the wrong permissions?如果文件夹已经存在,但权限错误,会发生什么?

The mkdir method has the ability to recursively create any directories in a path that don't exist, and ignore the ones that do. mkdir方法能够递归地创建路径中不存在的任何目录,并忽略存在的目录。

From the Node.js v10/11 documentation :来自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; });

NOTE: You'll need to import the built-in fs module first.注意:您需要先导入内置的fs模块。

Now here's a little more robust example that leverages native ECMAScript Modules (with flag enabled and.mjs extension), handles non-root paths, and accounts for full pathnames:现在这里有一个更强大的示例,它利用原生 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'); } }); }

You can use it like createDirectories('/components/widget/widget.js');您可以像createDirectories('/components/widget/widget.js');一样使用它. .

And of course, you'd probably want to get more fancy by using promises with async/await to leverage file creation in a more readable synchronous-looking way when the directories are created;当然,您可能希望通过使用带有 async/await 的 Promise 在创建目录时以更具可读性的同步方式利用文件创建来获得更多幻想; but, that's beyond the question's scope.但是,这超出了问题的 scope。

I have found an npm module that works like a charm for this.我找到了一个 npm 模块,它就像一个魅力。

It simply does a recursive mkdir when needed, like a "mkdir -p ".它只是在需要时执行递归mkdir ,例如“mkdir -p”。

With the fs-extra package you can do this with a one-liner :使用fs-extra package ,您可以使用单线执行此操作:

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

The one line version:单行版本:

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

You can just use mkdir and catch the error if the folder exists.如果文件夹存在,您可以只使用mkdir并捕获错误。
This is async (so best practice) and safe.这是异步的(因此是最佳实践)且安全。

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

(Optionally add a second argument with the mode.) (可以选择在模式中添加第二个参数。)


Other thoughts:其他想法:

  1. You could use then or await by using native promisify .您可以使用 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. You can make your own promise method, something like (untested):您可以制作自己的 promise 方法,例如(未经测试):

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

     fs.existsSync(path) || fs.mkdirSync(path)
  4. Or you can use a library, the two most popular being或者你可以使用一个库,其中两个最受欢迎的是

    • mkdirp (just does folders) mkdirp (只做文件夹)
    • fsextra (supersets fs, adds lots of useful stuff) fsextra (超集 fs,添加了很多有用的东西)

One-line solution: Creates the directory if it does not exist一线解决方案:如果目录存在则创建

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

The best solution would be to use the npm module called node-fs-extra .最好的解决方案是使用名为node-fs-extra的 npm 模块。 It has a method called mkdir which creates the directory you mentioned.它有一个名为mkdir的方法,用于创建您提到的目录。 If you give a long directory path, it will create the parent folders automatically.如果您提供较长的目录路径,它将自动创建父文件夹。 The module is a superset of npm module fs , so you can use all the functions in fs also if you add this module.该模块是 npm 模块fs的超集,因此如果添加此模块,您也可以使用fs中的所有功能。

Use:利用:

 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() is deprecated . fs.exist() 已弃用 So I have used fs.stat() to check the directory status.所以我使用fs.stat()来检查目录状态。 If the directory does not exist, fs.stat() throws an error with a message like 'no such file or directory'.如果目录不存在, fs.stat()会抛出错误,并显示类似“没有这样的文件或目录”的消息。 Then I have created a directory.然后我创建了一个目录。

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

solutions解决方案

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

update your package.json file config更新您的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 }) }

refs参考

NodeJS Version: v18.2.0 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://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 https://github.com/nodejs/help/issues/2907#issuecomment-757446568

ESM: ECMAScript modules ESM:ECMAScript 模块

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

With Node.js 10 + ES6 :使用 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; } } })()

I'd like to add a TypeScript Promise refactor of josh3736's answer .我想添加一个 TypeScript Promise 重构josh3736 的答案

It does the same thing and has the same edge cases.它做同样的事情并具有相同的边缘情况。 It just happens to use Promises, TypeScript typedefs, and works with "use strict".它恰好使用 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 } }); }); }

For node v10 and above对于节点 v10 及更高版本

As some answers pointed out, since node 10 you can use recursive:true for mkdir正如一些答案指出的那样,从节点 10 开始,您可以将recursive:true用于mkdir

What is not pointed out yet, is that when using recursive:true, mkdir does not return an error if the directory already existed .还没有指出的是,当使用 recursive:true 时,如果目录已经存在,mkdir 不会返回错误

So you can do:所以你可以这样做:

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

Using promises使用承诺

Also since node 10, you can get Promise versions of all fs functions by requiring from fs/promises同样从节点 10 开始,您可以通过要求fs/promises获得所有 fs 函数的Promise 版本

So putting those two things together, you get this simple solution:所以把这两件事放在一起,你会得到这个简单的解决方案:

 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

You can use the Node.js File System command fs.stat to check if a directory exists and fs.mkdir to create a directory with callback, or fs.mkdirSync to create a directory without callback, like this example:您可以使用 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); } }); };

I had to create sub-directories if they didn't exist.如果它们不存在,我必须创建子目录。 I used this:我用这个:

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

From the documentation this is how you do it asynchronously (and recursively):文档来看,这是您异步(和递归)执行此操作的方式:

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

Here is a little function to recursivlely create directories:这是一个小 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 } '') }

my solutions我的解决方案

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

update package.json config更新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 }

refs参考

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

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

Using async / await:使用异步/等待:

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

You will need to promisify fs :您需要承诺fs

 import nodeFs from 'fs'; import bluebird from 'bluebird'; const fs = bluebird.promisifyAll(nodeFs);

A function to do this asynchronously (adjusted from a similar answer on SO that used sync functions, that I can't find now)一个 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