ChatGPT解决这个技术问题 Extra ChatGPT

同步检查 Node.js 中是否存在文件/目录

如何使用 node.js 同步检查文件或目录是否存在?

同步操作非常适合在返回模块之前执行一次性文件/目录操作。例如,引导配置文件。
并非在所有情况下都具有热缓存的@PaulDraper。
无论性能如何,有时您只想以同步方式运行它以获得开发人员体验。例如,如果您将 Node 用于设计上应该是阻塞的数据处理脚本,那么在这种情况下 async exists 只会添加不必要的回调。
绝对 +1 对 Kunok 的声明。在我的其余代码中,我只在速度真正重要的瓶颈时才使代码更复杂。我为什么不把这个原则应用到文件阅读上呢?在许多程序的许多部分,代码的简单性/可读性可能比执行速度更重要。如果这是一个瓶颈区域,我将使用异步方法来防止停止进一步的代码执行。否则......同步很棒。不要盲目讨厌同步。
请...不要“值得注意”,因为用户明确询问如何同步进行。

T
T.J. Crowder

多年来,这个问题的答案发生了变化。当前答案位于顶部,然后是多年来按时间顺序排列的各种答案:

当前答案

您可以使用 fs.existsSync()

const fs = require("fs"); // Or `import fs from "fs";` with ESM
if (fs.existsSync(path)) {
    // Do something
}

它被弃用了几年,但不再是。从文档:

请注意,不推荐使用 fs.exists(),但不推荐使用 fs.existsSync()。 (fs.exists() 的回调参数接受与其他 Node.js 回调不一致的参数。fs.existsSync() 不使用回调。)

您已明确要求进行 同步 检查,但如果您可以改用 异步 检查(通常最好使用 I/O),请使用 fs.promises.access如果不是,则使用 async 函数或 fs.access(从 exists is deprecated 开始):

async 函数中:

try {
    await fs.promises.access("somefile");
    // The check succeeded
} catch (error) {
    // The check failed
}

或使用回调:

fs.access("somefile", error => {
    if (!error) {
        // The check succeeded
    } else {
        // The check failed
    }
});

历史答案

以下是按时间顺序排列的历史答案:

2010 年的原始答案(stat/statSync 或 lstat/lstatSync)

2012 年 9 月更新(exists/existsSync)

2015 年 2 月更新(注意 exists/existsSync 即将弃用,因此我们可能会回到 stat/statSync 或 lstat/lstatSync)

2015 年 12 月更新(还有 fs.access(path, fs.F_OK, function(){}) / fs.accessSync(path, fs.F_OK),但请注意,如果文件/目录不存在,则为错误; 如果您需要在不打开的情况下检查是否存在,fs.stat 的文档建议使用 fs.access)

2016 年 12 月更新 fs.exists() 仍被弃用,但 fs.existsSync() 不再被弃用。因此,您现在可以安全地使用它。

2010年的原始答案:

您可以使用 statSynclstatSync (docs link),它们会给您一个 fs.Stats object。通常,如果函数的同步版本可用,它将与异步版本具有相同的名称,并以 Sync 结尾。所以 statSyncstat 的同步版本; lstatSynclstat 的同步版本,等等。

lstatSync 告诉您某物是否存在,如果存在,它是文件还是目录(或在某些文件系统中,符号链接、块设备、字符设备等),例如,如果您需要知道它是否存在存在并且是一个目录:

var fs = require('fs');
try {
    // Query the entry
    stats = fs.lstatSync('/the/path');

    // Is it a directory?
    if (stats.isDirectory()) {
        // Yes it is
    }
}
catch (e) {
    // ...
}

...同样,如果是文件,则有 isFile;如果是块设备,则有isBlockDevice等。注意try/catch;如果该条目根本不存在,它将引发错误。

如果您不关心条目 是什么,而只想知道它是否存在,您可以使用 path.existsSync(或最新的 fs.existsSync)作为 noted by user618408:< /s>

var path = require('path'); if (path.existsSync("/the/path")) { // 或 fs.existsSync // ... }

它不需要 try/catch,但不会向您提供有关事物是什么的信息,只是告诉您它就在那里。 path.existsSync 很久以前就被弃用了。

旁注:您明确询问如何同步检查,因此我使用了上述函数的 xyzSync 版本。但只要有可能,对于 I/O,最好避免同步调用。从 CPU 的角度来看,调用 I/O 子系统需要大量时间。请注意调用 lstat 而不是 lstatSync 是多么容易:

// Is it a directory?
lstat('/the/path', function(err, stats) {
    if (!err && stats.isDirectory()) {
        // Yes it is
    }
});

但是,如果您需要同步版本,它就在那里。

2012 年 9 月更新

几年前的以下答案现在有点过时了。当前的方法是使用 fs.existsSync 对文件/目录的存在进行同步检查(当然也可以使用 fs.exists 进行异步检查),而不是使用下面的 path 版本。

例子:

var fs = require('fs');

if (fs.existsSync(path)) {
    // Do something
}

// Or

fs.exists(path, function(exists) {
    if (exists) {
        // Do something
    }
});

2015 年 2 月更新

到了 2015 年,Node 文档现在说 fs.existsSync(和 fs.exists)“将被弃用”。 (因为 Node 的人认为在打开某个东西之前检查它是否存在是愚蠢的;但那不是检查某个东西是否存在的唯一原因!)

所以我们可能会回到各种 stat 方法...当然,直到/除非这种情况再次发生变化。

2015 年 12 月更新

不知道它在那里多久了,但还有fs.access(path, fs.F_OK, ...) / fs.accessSync(path, fs.F_OK)。并且至少从 2016 年 10 月开始,fs.stat documentation 建议使用 fs.access 进行存在性检查(“要检查文件是否存在而不事后对其进行操作,建议使用 fs.access()。”)。但请注意,访问不可用被视为错误,因此如果您希望文件可访问,这可能是最好的:

var fs = require('fs');

try {
    fs.accessSync(path, fs.F_OK);
    // Do something
} catch (e) {
    // It isn't accessible
}

// Or

fs.access(path, fs.F_OK, function(err) {
    if (!err) {
        // Do something
    } else {
        // It isn't accessible
    }
});

2016 年 12 月更新

您可以使用 fs.existsSync()

if (fs.existsSync(path)) {
    // Do something
}

它被弃用了几年,但不再是。从文档:

请注意,不推荐使用 fs.exists(),但不推荐使用 fs.existsSync()。 (fs.exists() 的回调参数接受与其他 Node.js 回调不一致的参数。fs.existsSync() 不使用回调。)


path.exists 和 path.existsSync 都已被弃用,取而代之的是 fs.exists 和 fs.existsSync
“Node 的人认为在打开某个东西之前检查它是否存在是愚蠢的,它确实存在;”为什么检查文件是否存在是愚蠢的?
@PetrHurtak:不是总是(因为检查存在的原因有很多),但如果你要打开文件,最好只发出open 调用并处理异常或找不到文件时的任何处理。毕竟,现实世界是混乱的:如果你先检查它就在那里,那并不意味着当你试图打开它时它仍然在那里;如果您先检查并且它不存在,那并不意味着它稍后不会存在。像这样的时间安排看起来像是边缘情况,但它们总是出现。所以如果你要打开,先检查没有意义。
在这里,我认为将错误用于控制流是一种反模式:link
支持更新答案,非常棒。愿更多人这样做。
C
Community

查看源代码,有一个同步版本的 path.exists - path.existsSync。看起来它在文档中被遗漏了。

更新:

path.existspath.existsSync 现在已弃用请使用 fs.existsfs.existsSync

2016 年更新:

fs.exists fs.existsSync 已被弃用。请改用 fs.stat()fs.access()

2019 年更新:

使用 fs.existsSync。它没有被弃用。 https://nodejs.org/api/fs.html#fs_fs_existssync_path


path.existsSync(p) 在 0.4.10 文档中nodejs.org/docs/v0.4.10/api/path.html
实际上,一个更新的答案: path.existsSync 已弃用。它现在称为 fs.existsSync
现在文档说 fs.exists 将被弃用。 nodejs.org/api/fs.html#fs_fs_existssync_path
我写了一个小库来替换旧的 exists 函数:is-there
当前文档(版本 ~9)仅将 fs.exists 标记为已弃用,而 fs.existsSync 未标记!
B
BobDickinson

使用当前推荐的(截至 2015 年)API(根据节点文档),这就是我所做的:

var fs = require('fs');

function fileExists(filePath)
{
    try
    {
        return fs.statSync(filePath).isFile();
    }
    catch (err)
    {
        return false;
    }
}

针对@broadband 在评论中提出的 EPERM 问题,这提出了一个很好的观点。在许多情况下,fileExists() 可能不是考虑这一点的好方法,因为 fileExists() 不能真正保证布尔返回。您可能能够明确确定该文件是否存在,但您也可能会收到权限错误。权限错误并不一定意味着该文件存在,因为您可能缺乏对包含您正在检查的文件的目录的权限。当然,在检查文件是否存在时,您可能会遇到其他一些错误。

所以我上面的代码确实是doesFileExistAndDoIHaveAccessToIt(),但你的问题可能是dosFileNotExistAndCouldICreateIt(),这将是完全不同的逻辑(这需要考虑EPERM错误等)。

虽然 fs.existsSync 答案直接解决了此处提出的问题,但这通常不是您想要的(您不只是想知道“某物”是否存在于路径中,您可能关心“事物”是否存在存在的是文件或目录)。

底线是,如果您正在检查文件是否存在,您可能正在这样做,因为您打算根据结果采取一些行动,并且该逻辑(检查和/或后续行动)应该适应这个想法在该路径找到的东西可能是文件或目录,并且您在检查过程中可能会遇到 EPERM 或其他错误。


不错,我加了|| isDirectory() 使其成为文件/文件夹检查器。 var stats = fs.statSync(filePath);return stats.isFile() || stats.isDirectory();
如果程序无权访问该文件,即使文件存在,它仍然返回 false,即从文件 chmod ugo-rwx file.txt 或 Windows 右键单击中删除所有 rigts ... 异常消息:异常 fs.statSync (./ f.txt):错误:EPERM:不允许操作,stat 'X:\f.txt'。所以这种情况没有被上层代码覆盖。
哇,JS有时是迟钝的。可以肯定的是,97% 的时间您将使用该文件,但没有为 3% 提供简单的 file.exists() 实用程序,而是迫使我们将其包装在 try catch 中?变得真实......当天的婊子。
M
Melbourne2991

另一个更新

我自己需要这个问题的答案我查看了节点文档,似乎你不应该使用 fs.exists,而是使用 fs.open 并使用输出错误来检测文件是否不存在:

来自文档:

fs.exists() 是不合时宜的,仅出于历史原因而存在。几乎不应该有理由在您自己的代码中使用它。特别是,在打开文件之前检查文件是否存在是一种反模式,这会使您容易受到竞争条件的影响:另一个进程可能会在调用 fs.exists() 和 fs.open() 之间删除文件。只需打开文件并在错误不存在时处理它。

http://nodejs.org/api/fs.html#fs_fs_exists_path_callback


有没有办法用 openSync 来做,而不是打开
@GregHornby 我想它应该以与 openSync 相同的方式工作
对于那些仍然需要 existsexistsSync 的人,我创建了 is-there
这种弃用让我很烦。当只需要知道文件的存在时,打开文件只是为了查看是否引发错误似乎是一种资源浪费。
或者只是在创建模式下打开文件并锁定它不被其他进程使用(这会阻止它被删除,直到锁定它的进程删除它)。
b
broadband

我使用下面的函数来测试文件是否存在。它还捕获其他异常。因此,如果存在权限问题,例如 chmod ugo-rwx filename 或 Windows Right Click -> Properties -> Security -> Advanced -> Permission entries: empty list .. 函数应返回异常。该文件存在,但我们无权访问它。忽略这类异常是错误的。

function fileExists(path) {

  try  {
    return fs.statSync(path).isFile();
  }
  catch (e) {

    if (e.code == 'ENOENT') { // no such file or directory. File really does not exist
      console.log("File does not exist.");
      return false;
    }

    console.log("Exception fs.statSync (" + path + "): " + e);
    throw e; // something else went wrong, we don't have rights, ...
  }
}

异常输出,nodejs errors documentation 以防文件不存在:

{
  [Error: ENOENT: no such file or directory, stat 'X:\\delsdfsdf.txt']
  errno: -4058,
  code: 'ENOENT',
  syscall: 'stat',
  path: 'X:\\delsdfsdf.txt'
}

例外情况是我们没有文件的权限,但存在:

{
  [Error: EPERM: operation not permitted, stat 'X:\file.txt']
  errno: -4048,
  code: 'EPERM',
  syscall: 'stat',
  path: 'X:\\file.txt'
}

真的是这样,这是自节点弃用最后 37 种执行此操作的方法以来为数不多的最新答案之一
呸,你打败了我。如果我读过这篇文章,我可以节省一些时间。
很有用!谢谢
g
gsalgadotoledo

fs.exists() 已弃用不要使用它https://nodejs.org/api/fs.html#fs_fs_exists_path_callback

您可以实现在此使用的核心 nodejs 方式:https://github.com/nodejs/node-v0.x-archive/blob/master/lib/module.js#L86

function statPath(path) {
  try {
    return fs.statSync(path);
  } catch (ex) {}
  return false;
}

这将返回 stats 对象,然后一旦您获得了 stats 对象,您就可以尝试

var exist = statPath('/path/to/your/file.js');
if(exist && exist.isFile()) {
  // do something
}

V
Vijay
const fs = require('fs');

检查如下功能,

if(fs.existsSync(<path_that_need_to_be_checked>)){
  // enter the code to excecute after the folder is there.
}
else{
  // Below code to create the folder, if its not there
  fs.mkdir('<folder_name>', cb function);
}

j
jstice4all

这里的一些答案说 fs.existsfs.existsSync 都已弃用。根据文档,这不再是真的。现在只弃用 fs.exists

请注意,不推荐使用 fs.exists(),但不推荐使用 fs.existsSync()。 (fs.exists() 的回调参数接受与其他 Node.js 回调不一致的参数。fs.existsSync() 不使用回调。)

因此,您可以安全地使用 fs.existsSync() 来同步检查文件是否存在。


I
Ivo Wetzel

path 模块不提供 path.exists 的同步版本,因此您必须使用 fs 模块。

我能想象的最快的事情是使用 fs.realpathSync,它会抛出一个您必须捕获的错误,因此您需要使用 try/catch 创建自己的包装函数。


佚名

使用 fileSystem (fs) 测试将触发错误对象,然后您需要将其包装在 try/catch 语句中。节省一些精力,并使用 0.4.x 分支中引入的功能。

var path = require('path');

var dirs = ['one', 'two', 'three'];

dirs.map(function(dir) {
  path.exists(dir, function(exists) {
    var message = (exists) ? dir + ': is a directory' : dir + ': is not a directory';
    console.log(message);
  });
});

path.exists 现在在 fs 下,所以它是 fs.exists(path, callback)
C
Community

为那些“正确”指出它并没有直接回答问题的人更新了答案,更多的是带来了另一种选择。

同步解决方案:

fs.existsSync('filePath')see docs here

如果路径存在则返回真,否则返回假。

异步承诺解决方案

在异步上下文中,您可以使用 await 关键字在同步方法中编写异步版本。你可以简单地将异步回调方法变成这样的 Promise:

function fileExists(path){
  return new Promise((resolve, fail) => fs.access(path, fs.constants.F_OK, 
    (err, result) => err ? fail(err) : resolve(result))
  //F_OK checks if file is visible, is default does no need to be specified.

}

async function doSomething() {
  var exists = await fileExists('filePath');
  if(exists){ 
    console.log('file exists');
  }
}

the docs 在 access() 上。


OP想要一个同步解决方案
您应该将代码更新为 function asyncFileExists(path) { //F_OK checks if file is visible, is default does no need to be specified. return new Promise(function (res, rej) { fs.access( path, fs.constants.F_OK, function (err) { err ? rej(err) : res(true); }, ); }); }
a
abrahamcalf

这已经得到解答,但如果您喜欢安装模块,您可以使用 dtfe,它代表

文件是否存在?

const dtfe = require('dtfe');
 
dtfe('package.json');
//=> true

F
Fisheiyy

您可以使用 fs-extra (npm i fs-extra) 及其 fs.ensureFile 或目录 fs.ensureDir,因为 fs.exists 已被贬低,并且 fs.access 不建议您在使用后编辑该文件“不要在调用 fs.open()、fs.readFile() 或 fs.writeFile() 之前使用 fs.access() 检查文件的可访问性。这样做会引入竞争条件,因为其他进程可能会在“


G
Grallen

fs.stat() 上的文档说如果您不打算操作该文件,请使用 fs.access()。它没有给出理由,可能是更快或更少的内存使用?

我使用节点进行线性自动化,所以我想我共享我用来测试文件存在的函数。

var fs = require("fs");

function exists(path){
    //Remember file access time will slow your program.
    try{
        fs.accessSync(path);
    } catch (err){
        return false;
    }
    return true;
}

Y
Yairopro

❄️ 你可以使用 graph-fs

directory.exists // boolean

T
Timothy C. Quinn

这是一个简单的包装解决方案:

var fs = require('fs')
function getFileRealPath(s){
    try {return fs.realpathSync(s);} catch(e){return false;}
}

用法:

适用于目录和文件

如果 item 存在,则返回文件或目录的路径

如果 item 不存在,则返回 false

例子:

var realPath,pathToCheck='<your_dir_or_file>'
if( (realPath=getFileRealPath(pathToCheck)) === false){
    console.log('file/dir not found: '+pathToCheck);
} else {
    console.log('file/dir exists: '+realPath);
}

确保使用 === 运算符来测试 return 是否等于 false。 fs.realpathSync() 在适当的工作条件下返回 false 没有任何合乎逻辑的理由,所以我认为这应该 100% 有效。

我希望看到一个不会产生错误并导致性能下降的解决方案。从 API 的角度来看, fs.exists() 似乎是最优雅的解决方案。


@丹,谢谢。我删除了截断的文本。我不记得那张纸条是什么了。如果它来了,我会添加注释。
NP。我正在删除我的评论。
j
jgmjgm

从答案看来,没有官方的 API 支持(如直接和明确的检查)。许多答案说要使用 stat,但是它们并不严格。例如,我们不能假设 stat 抛出的任何错误都意味着某些东西不存在。

假设我们尝试使用不存在的东西:

$ node -e 'require("fs").stat("god",err=>console.log(err))'
{ Error: ENOENT: no such file or directory, stat 'god' errno: -2, code: 'ENOENT', syscall: 'stat', path: 'god' }

让我们用一些存在但我们无权访问的东西来试试:

$ mkdir -p fsm/appendage && sudo chmod 0 fsm
$ node -e 'require("fs").stat("fsm/appendage",err=>console.log(err))'
{ Error: EACCES: permission denied, stat 'access/access' errno: -13, code: 'EACCES', syscall: 'stat', path: 'fsm/appendage' }

至少你会想要:

let dir_exists = async path => {
    let stat;
    try {
       stat = await (new Promise(
           (resolve, reject) => require('fs').stat(path,
               (err, result) => err ? reject(err) : resolve(result))
       ));
    }
    catch(e) {
        if(e.code === 'ENOENT') return false;
        throw e;
    }

    if(!stat.isDirectory())
        throw new Error('Not a directory.');

    return true;
};

这个问题不清楚你是否真的希望它是同步的,或者你是否只希望它被写成好像它是同步的。此示例使用 await/async,因此它仅同步写入但异步运行。

这意味着您必须在顶层这样称呼它:

(async () => {
    try {
        console.log(await dir_exists('god'));
        console.log(await dir_exists('fsm/appendage'));
    }
    catch(e) {
        console.log(e);
    }
})();

另一种方法是在异步调用返回的承诺上使用 .then 和 .catch,如果您需要它的话。

如果您想检查某些东西是否存在,那么最好确保它是正确类型的东西,例如目录或文件。这包含在示例中。如果不允许它是符号链接,则必须使用 lstat 而不是 stat 因为 stat 将自动遍历链接。

您可以在此处替换所有异步同步代码,并改用 statSync。然而,期望一旦 async 和 await 成为普遍支持,Sync 调用最终将变得多余而被贬值(否则你将不得不在任何地方和链上定义它们,就像使用 async 一样,让它变得毫无意义)。


原始问题没有具体说明。我也在演示如何明确地做事。由于缺乏明确性,许多答案可能会导致错误。人们经常希望对事物进行编程,使其看起来是同步的,但不一定需要同步执行。 statSync 与我演示的代码不同。对实际需要的任何描述都是模棱两可的,因此您只是在强加您的个人解释。如果您找到不理解的答案,最好在评论或 PM 中询问以了解需要进行哪些编辑。
如果您愿意,您也可以窃取我的代码示例,适当命名,将其放在 github 上,将其添加到 npm,然后答案将只有一行/链接:D。
为了举例,代码很短,但欢迎您提交编辑建议以包含 && !isFile 或检查符号链接等(尽管问题从未明确说明,即使这是他们想要的)。正如我已经指出的那样,我的回答满足对问题的一种解释,并且与您的单行提案所做的事情不同。
S
SwiftNinjaPro

很有可能,如果您想知道某个文件是否存在,您计划在存在时要求它。

function getFile(path){
    try{
        return require(path);
    }catch(e){
        return false;
    }
}