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

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

920767 次浏览

这个问题的答案多年来一直在变化。当前的答案在顶部,其次是多年来按时间顺序排列的各种答案:

当前答案

您可以使用#0

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

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

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

您特别要求进行同步检查,但如果您可以使用异步检查(通常最好使用I/O),如果您使用async函数,请使用#0,如果不是,请使用#2(从#3已弃用开始):

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文档链接),这会给你一个#2对象。一般来说,如果一个函数的同步版本可用,它将与异步版本同名,最后是Sync。所以statSyncstat的同步版本;lstatSynclstat的同步版本,等等。

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

var fs = require('fs');try {// Query the entrystats = fs.lstatSync('/the/path');
// Is it a directory?if (stats.isDirectory()) {// Yes it is}}catch (e) {// ...}

…类似地,如果它是一个文件,有isFile;如果它是一个块设备,有isBlockDevice,等等,等等注意try/catch;如果条目根本不存在,它会抛出错误。

如果你不在乎条目是什么,只想知道它是否存在,你可以使用#0(或最新的fs.existsSync)作为注意到用户618408

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

它不需要try/catch,但不会给你任何关于这个东西是什么的信息,只是它在那里。


旁注:你已经明确要求如何检查同步,所以我使用了上面函数的xyzSync版本。但只要有可能,在I/O中,最好避免同步调用。从CPU的角度来看,调用I/O子系统需要很长时间。请注意调用#1而不是lstatSync是多么容易:

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

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

2012年9月更新

下面几年前的答案现在有点过时了。当前的方法是使用#0进行文件/目录存在的同步检查(或者当然是#1进行异步检查),而不是下面的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月更新

不知道它在那里多久了,但也有#0/#1。至少截至2016年10月,#2留档建议使用fs.access进行存在检查(“要检查文件是否存在而不随后操作它,建议使用#4。”)。但请注意,访问不可用被认为是错误,所以如果你希望文件可以访问,这可能是最好的:

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月更新

您可以使用#0

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

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

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

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

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

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

更新时间:

path.existspath.existsSync现在是已弃用请使用#2和#3

2016年更新:

fs.exists#1也有被弃用。使用fs.stat()fs.access()代替。

2019年更新:

使用fs.existsSync。不建议使用。https://nodejs.org/api/fs.html#fs_fs_existssync_path

使用fileSystem(fs)测试将触发错误对象,然后您需要将其包装在try/cat语句中。节省一些精力,并使用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);});});

另一个更新

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

从文档:

fs.exists()是一个时代错误,只存在于历史原因。几乎不应该有理由在自己的代码中使用它。

特别是,在打开文件之前检查文件是否存在是反模式让你容易受到种族条件的影响:另一个进程可以在调用fs.exists()和fs.open()。只需打开文件并处理错误在那里。

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

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

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

用法:

  • 适用于目录和文件
  • 如果项目存在,它返回文件或目录的路径
  • 如果项目不存在,则返回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);}

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

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

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

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

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

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

虽然fs.existsSync的答案直接回答了这里提出的问题,但这通常不是你想要的(你不仅仅想知道路径上是否存在“某物”,你可能关心存在的“某物”是文件还是目录)。

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

我使用下面的函数来测试文件是否存在。它还捕获其他异常。所以如果有权限问题,例如chmod ugo-rwx filename或WindowsRight 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 existconsole.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, ...}}

异常输出,case文件不存在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'}

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}

#0上的文档说,如果您不打算操作文件,请使用#1。它没有给出理由,可能更快或更少内存使用?

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

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.existsfs.existsSync都已弃用。根据文档,这不再是真的。现在只有fs.exists被弃用:

请注意,fs.exists()已弃用,但fs.existsSync()不是fs.exists()的回调参数接受以下参数与其他Node.js回调不一致。fs.existsSync()不会使用回调。)

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

更新后的asnwer对于那些正确指出它并不能直接回答问题的人来说,更带来了另一种选择。

同步解决方案:

fs.existsSync('filePath')也是看这里的文档

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

异步Promise解决方案

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

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

的文档访问()。

从答案来看,似乎没有官方的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;};

问题不清楚您是真的希望它是同步的,还是只希望它被编写为同步的。此示例使用wait/async,因此它只是同步写入但异步运行。

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

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

另一种方法是在从async调用返回的Promise上使用. time和. cat,如果您需要它的话。

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

你可以在这里替换所有的async to sync代码并使用statSync代替。然而,一旦async和wait得到普遍支持,Sync调用将变得多余,最终被贬值(否则你将不得不在任何地方定义它们,就像async一样,使其变得毫无意义)。

机会是,如果您想知道文件是否存在,您计划在它存在时要求它。

function getFile(path){try{return require(path);}catch(e){return false;}}
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 therefs.mkdir('<folder_name>', cb function);}

这已经回答了,但是如果你喜欢安装模块,你可以使用#0,它代表

文件是否存在?

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

您可以使用fs-额外(npm i fs-额外)及其fs.ensureFile或目录fs.ensureDir,因为fs.exists已被删除,fs.access不建议您在使用后编辑该文件"在调用fs.open()、fs.readFile()或fs.writeFile()之前,不要使用fs.access()检查文件的可访问性。这样做会引入竞争条件,因为其他进程可能会在两次调用之间更改文件的状态。相反,用户代码应直接打开/读取/写入文件并处理如果文件不可访问时引发的错误。

❄️你可以使用graph-fs

directory.exists // boolean