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


1207

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


57
同步操作非常适合在返回模块之前执行一次性文件/目录操作。例如,引导配置文件。
jocull

1
具有温暖缓存的 @PaulDraper 并非在所有情况下都是正确的。
mikemaccana '17

11
无论性能如何,有时您只是想以同步方式运行它以获取开发人员的经验。例如,如果将Node用于数据处理脚本,该脚本在设计上应被阻止,那么在这种情况下,异步exists只会添加不必要的回调。
Kunok

2
绝对对Kunok的陈述+1。在我的其余代码中,只有在速度真正重要的瓶颈时,我才使代码更复杂。为什么我不将该原理应用于文件读取?在许多程序的许多部分中,代码的简单性/可读性比执行速度更为重要。如果这是瓶颈区域,我将使用异步方法来阻止进一步的代码执行。否则...同步很棒。不要盲目讨厌同步。
BryanGrezeszak

3
请...不要“值得注意”,因为用户会明确地询问如何同步进行。
jClark

Answers:


2238

多年来,这个问题的答案已经改变。在目前的答案是在此间举行的顶部,然后用按时间顺序多年的各种答案:

当前答案

您可以使用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不赞成使用),请使用:

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/ statSynclstat/ lstatSync
  • 2012年9月更新
    exists/ existsSync
  • 2015年2月更新
    (注意到即将弃用exists/ existsSync,因此我们很可能回到stat/ statSynclstat/ 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文档链接),它为您提供一个fs.Stats对象。通常,如果功能的同步版本可用,则其名称与Sync结尾的异步版本相同。所以,statSync是的同步版本stat; lstatSync是等的同步版本lstat

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;如果该条目根本不存在,则会引发错误。

如果您不在乎该条目什么,而只想知道它是否存在,则可以使用user618408指出的path.existsSync(或使用最新的fs.existsSync):

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

它不需要a,try/catch但不会为您提供任何有关事物的信息,只是它在那里。 path.existsSync很久以前不推荐使用。


旁注:您已经明确询问了如何进行同步检查,因此我使用了xyzSync上述功能的版本。但是,只要有可能,使用I / O最好避免同步调用。从CPU的角度来看,调用I / O子系统要花费大量时间。请注意调用lstatlstatSync

// 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文档建议使用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()不使用回调。)


7
不赞成使用path.exists和path.existsSync,而赞成使用fs.exists和fs.existsSync
Drew

15
“节点人员认为在打开某个东西之前先检查是否存在是愚蠢的;” 为什么检查文件是否存在很笨?
Petr Hurtak

32
@PetrHurtak:并不总是这样(因为有很多检查存在性的原因),但是如果您要打开文件,最好只是发出open调用并处理异常,或者如果文件不是找到了。毕竟,现实世界是混乱的:如果先检查一下就可以了,但这并不意味着当您尝试打开它时它仍然会存在。如果您先检查却不存在,那并不意味着稍后不会存在。像这样的计时事情似乎很极端,但是它们总是出现。因此,如果您要打开,则没有必要先检查。
TJ Crowder

13
在这里,我认为将错误用于控制流是一种反模式: link
argyle

4
@jeromeyers:可以,但是Ionică 已经为您做到了(请参阅上面的评论)。:-)
TJ Crowder 2015年

124

从源代码上看,有一个同步版本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


1
path.existsSync(p)位于0.4.10 docs nodejs.org/docs/v0.4.10/api/path.html
Paul Beusterien 2011年

21
实际上,不建议使用最近的答案:path.existsSync。现在称为fs.existsSync
Olivier Lalonde'3

9
现在文档说fs.exists将被弃用。nodejs.org/api/fs.html#fs_fs_existssync_path
Greg Hornby,

我写了一个小程序来替换旧的exists功能:is-there
尼卡比曹

6
当前文档(版本〜9)仅被标记fs.exists为已弃用,而fs.existsSync不是!
Kunok

57

使用目前推荐的(截至2015年)API(根据Node docs),这是我的工作:

var fs = require('fs');

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

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

因此,我上面的代码实际上是dosFileExistAndDoIHaveAccessToIt(),但您的问题可能是dosFileNotExistAndCouldICreateIt(),这将是完全不同的逻辑(这需要考虑EPERM错误)。

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

最重要的是,如果您要检查文件是否存在,则可能要这样做,因为您打算根据结果采取一些措施,并且该逻辑(检查和/或后续措施)应符合该想法。在该路径上找到的东西可能是文件或目录,并且在检查过程中可能遇到EPERM或其他错误。


4
很好,我加了|| isDirectory()使其成为文件/文件夹检查器。var stats = fs.statSync(filePath); return stats.isFile()|| stats.isDirectory();
鲍勃

4
如果程序没有访问文件的权限,即使文件存在,它仍然返回false,即从文件chmod ugo-rwx file.txt或Windows中删除所有绑定。右键单击...异常消息:异常fs.statSync(./ f.txt):错误:EPERM:不允许操作,状态为“ X:\ f.txt”。因此,这种情况不包含在上面的代码中。
宽带

2
哇,JS有时会智障。可以肯定的是,您有97%的时间会使用该文件,但file.exists()3%的用户没有简单的工具,而是迫使我们将其包装在try catch中吗?变得真实...今日的itch子。
被驱逐的

20

另一个更新

我本人需要查找此节点文档,因此需要自己回答此问题,似乎您应该使用fs.exists,而应使用fs.open并使用输出的错误来检测文件是否不存在:

从文档:

fs.exists()是过时的,仅出于历史原因存在。几乎永远没有理由在自己的代码中使用它。

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

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


1
有没有办法用openSync来做到这一点,而不是打开
Greg Hornby

1
@GregHornby我想它应该与openSync以相同的方式工作
Melbourne2991

2
对于那些仍然需要的东西existsexistsSync我创造了is-there
爱奥尼亚·比索(IonicăBizău)

6
这种贬低使我烦恼。当仅需要了解文件的存在时,仅为了查看是否引发错误而打开文件似乎是对资源的浪费。
Josh Hansen

11

我使用下面的功能来测试文件是否存在。它也捕获其他异常。因此,如果存在权限问题,例如chmod ugo-rwx filenameWindows或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错误文档

{
  [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'
}

2
真的是这样,这是最新的答案之一,因为node已弃用了最后的37种方法
1mike12 2016年

ah,你击败了我。如果我读过这篇,我本可以节省一些时间。
jgmjgm

5

不建议使用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对象,然后一旦获得s​​tats对象,您可以尝试

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

4

这里有一些答案,fs.exists并且fs.existsSync都已弃用。根据文档,这不再是真的。fs.exists现在仅受尊重:

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

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


3

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

我能想象的最快的方法是使用fs.realpathSync它将引发必须捕获的错误,因此您需要使用try / catch来创建自己的包装函数。


1

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

2
path.exists现在在fs下,因此它是fs.exists(path,callback)
Todd Moses

0

如果您不打算操纵文件,则上面的文件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;
}

0

为那些人“正确地”更新了答案,指出它并不能直接回答问题,更多地带来了替代选择。

同步解决方案:

fs.existsSync('filePath')可以在这里查看文档

如果路径存在,则返回true,否则返回false。

异步承诺解决方案

在异步上下文中,您可以使用await关键字以sync方法编写异步版本。您可以简单地将异步回调方法转换为如下的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');
  }
}

access()上的文档


1
OP需要一个同步解决方案
vdegenne

您应该将代码更新为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); }, ); }); }
pery mimon

0

如果您想知道文件是否存在,则有可能需要它。

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

-1

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

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

用法:

  • 适用于目录和文件
  • 如果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()似乎是最优雅的解决方案。


1
@丹,谢谢。我删除了截断的文本。我不记得那张纸条是什么。如果出现我,我会添加笔记。
蒂莫西·奎因

1
Np。我要删除我的评论。
Dan Dascalescu 2015年

-2

从答案中可以看出,对此没有官方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);
    }
})();

另一种选择是,如果需要进一步调用,则对异步调用返回的promise使用.then和.catch。

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

您可以在此处替换所有异步来同步代码,而改用statSync。但是,请期望一旦异步和等待得到普遍支持,同步调用将最终变得多余,从而变得不受欢迎(否则,您将不得不在各处和整个链上进行定义,就像使用异步使它变得毫无意义一样)。


1
原始问题未指定。我也正在明确演示如何做事。由于缺乏清晰性,许多答案可能会导致错误。人们经常想对事物进行编程,使其看起来是同步的,但不一定要同步执行。statSync与我演示的代码不同。关于实际需要的两种说法都模棱两可,因此您仅是施加个人解释。如果您找不到答案,最好只是在评论或项目经理中提出,以进行所需的编辑。
jgmjgm

1
如果您还希望窃取我的代码示例,请对其适当地命名,将其放在github上,将其添加到npm中,然后答案将仅为一行/链接:D。
jgmjgm

出于示例的目的,该代码很短,但是欢迎您提交编辑建议,以包括&&!isFile或检查符号链接等(同样,尽管该问题从未明确指出,即使他们想要的是)。正如我已经指出的那样,我的答案满足了对该问题的一种解释,并且与您的一线提案没有做相同的事情。
jgmjgm
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.