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

Check synchronously if file/directory exists in Node.js

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


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

您可以使用fs.existsSync():好的。

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

它被弃用了好几年,但现在不再是了。来自文档:好的。

Note that fs.exists() is deprecated, but fs.existsSync() is not. (The
callback parameter to fs.exists() accepts parameters that are
inconsistent with other Node.js callbacks. fs.existsSync() does not
use a callback.)

Ok.

您特别要求进行同步检查,但是如果您可以使用异步检查(通常最好使用I/O),如果您使用的是async函数,则使用fs.promises.access,如果不使用,则使用fs.access(因为exists)(因为fs.exists)(因为fs.exists)被否决):好的。

async函数中:好的。

1
2
3
4
5
6
try {
    await fs.promises.access("somefile");
    // The check succeeded
} catch (error) {
    // The check failed
}

或通过回拨:好的。

1
2
3
4
5
6
7
fs.access("somefile", error => {
    if (!error) {
        // The check succeeded
    } catch (error) {
        // The check failed
    }
});

历史答案

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

  • 2010年的原始答案(stat/statSynclstat/lstatSync)
  • 2012年9月更新(exists/existsSync)
  • 2015年2月更新(注意到exists/existsSync的折旧迫在眉睫,所以我们可能会回到stat/statSynclstatlstatSync的版本)
  • 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对象。通常,如果函数的同步版本可用,那么它将与末尾带有Sync的异步版本同名。所以statSyncstat的同步版本;lstatSynclstat的同步版本等。好的。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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,如user618408所述:好的。

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

它不需要使用try/catch,但是它没有提供任何关于事物是什么的信息,只是它在那里。path.existsSync很久以前就被弃用了。好的。

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

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

但如果您需要同步版本,它就在那里。好的。2012年9月更新

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

例子:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
var fs = require('fs');

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

// Or

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

2015年2月更新

这里我们是在2015年,节点文档现在说EDOCX1(和fs.exists)"将被否决"。(因为节点人员认为在打开某个东西之前检查它是否存在是愚蠢的,而这并不是检查某个东西是否存在的唯一原因!)好的。

所以我们可能会回到各种各样的stat方法…当然,除非这种情况再次改变。好的。2015年12月更新

不知道有多久了,但也有fs.access(path, fs.F_OK, ...)fs.accessSync(path, fs.F_OK)。至少截至2016年10月,fs.stat文件建议使用fs.access进行存在性检查("为了检查文件是否存在,而不随后对其进行操作,建议使用fs.access())"。但是请注意,访问不可用被认为是一个错误,因此如果您希望文件可以访问,那么这可能是最好的方法:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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():好的。

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

它被弃用了好几年,但现在不再是了。来自文档:好的。

Note that fs.exists() is deprecated, but fs.existsSync() is not. (The
callback parameter to fs.exists() accepts parameters that are
inconsistent with other Node.js callbacks. fs.existsSync() does not
use a callback.)

Ok.

好啊。


从源代码来看,有一个同步版本的path.exists--path.existsSync。看起来好像在医生那里找不到。

更新:

path.existspath.existsSync现在已被否决。请使用fs.existsfs.existsSync

更新2016:

fs.existsfs.existsSync也被否决。改用fs.stat()或fs.access()。


使用当前推荐的(截至2015年)API(每个节点文档),这是我要做的:

1
2
3
4
5
6
7
8
9
10
11
12
13
var fs = require('fs');

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

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

所以我上面的代码实际上是doesfileexistendodiohaveaccesstoit(),但您的问题可能是doesfilenotexistandcouldicreateit(),这将是完全不同的逻辑(这将需要考虑eperm错误等)。

虽然fs.existssync答案直接解决了这里提出的问题,但这通常不是您想要的(您不只是想知道路径上是否存在"某物",您可能关心存在的"某物"是文件还是目录)。

底线是,如果您正在检查文件是否存在,那么您可能会这样做,因为您打算根据结果采取一些操作,并且该逻辑(检查和/或后续操作)应该适应这样的想法:在该路径上找到的某个东西可能是一个文件或目录,并且在检查的过程。


另一个更新

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

来自文档:

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.

http://nodejs.org/api/fs.html fs fs exists path回调


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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错误文档:

1
2
3
4
5
6
7
{
  [Error: ENOENT: no such file or directory, stat 'X:\\delsdfsdf.txt']
  errno: -4058,
  code: 'ENOENT',
  syscall: 'stat',
  path: 'X:\\delsdfsdf.txt'
}

例外情况:如果我们没有对该文件的权限,但存在:

1
2
3
4
5
6
7
{
  [Error: EPERM: operation not permitted, stat 'X:\file.txt']
  errno: -4048,
  code: 'EPERM',
  syscall: 'stat',
  path: 'X:\\file.txt'
}


这里的一些答案说,fs.existsfs.existsSync都被否决了。根据文件,这不再是真的了。现在只有fs.exists被折旧:

Note that fs.exists() is deprecated, but fs.existsSync() is not. (The
callback parameter to fs.exists() accepts parameters that are
inconsistent with other Node.js callbacks. fs.existsSync() does not
use a callback.)

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


fs.exists()已弃用,请不要使用它https://nodejs.org/api/fs.html fs exists path回调

您可以使用以下方法实现核心节点:https://github.com/node js/node-v0.x-archive/blob/master/lib/module.js_l86

1
2
3
4
5
6
function statPath(path) {
  try {
    return fs.statSync(path);
  } catch (ex) {}
  return false;
}

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

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

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

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


fs.stat()上的文档说,如果不想操纵文件,就使用fs.access()。它没有给出理由,可能会更快或更少地使用备忘录?

我使用node进行线性自动化,所以我认为我共享了用于测试文件存在性的功能。

1
2
3
4
5
6
7
8
9
10
11
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;
}

使用文件系统(fs)测试将触发错误对象,然后需要用try/catch语句包装这些对象。节省一些精力,并使用0.4.x分支中介绍的功能。

1
2
3
4
5
6
7
8
9
10
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);
  });
});


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

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

用途:

  • 适用于目录和文件
  • 如果项存在,则返回文件或目录的路径
  • 如果项不存在,则返回false

例子:

1
2
3
4
5
6
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);
}

确保使用==运算符测试返回值是否等于false。在适当的工作条件下,fs.realpathsync()不会返回false,因此我认为这应该100%有效。

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


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

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

1
2
$ 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' }

让我们尝试一些存在但我们无法访问的内容:

1
2
3
$ 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' }

至少你会想要:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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,以便它只同步写入,但异步运行。

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

1
2
3
4
5
6
7
8
9
(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和Wait得到普遍支持,同步调用将成为多余的,最终会被贬低(否则,您必须在每个地方和链上定义它们,就像Async一样,使其真正没有意义)。


为那些"正确"的人更新了asnewr,指出它并没有直接回答问题,更多的是带来一个备选方案。

同步解决方案:

fs.existsSync('filePath')也见这里的文件。

Returns true if the path exists, false otherwise.

异步承诺解决方案

在异步上下文中,您可以使用await关键字在sync方法中编写异步版本。您只需将异步回调方法转换为如下承诺:

1
2
3
4
5
6
7
8
9
10
11
12
13
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()上的文档。