如何使用node.js同步检查文件或目录是否存在?
18 回答
多年来,这个问题的答案发生了变化。当前答案位于顶部,然后是多年来按时间顺序排列的各种答案:
当前答案
您可以使用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
/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年的原始答案:
您可以使用statSync
or lstatSync
(文档链接),它会为您提供一个fs.Stats
object。通常,如果函数的同步版本可用,它将与异步版本具有相同的名称,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
; 如果该条目根本不存在,它将引发错误。
如果您不关心条目是什么,只想知道它是否存在,您可以使用path.existsSync
(或使用 latest, fs.existsSync
),如user618408 所述:
var path = require('path');
if (path.existsSync("/the/path")) { // or fs.existsSync
// ...
}
它不需要 a 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
文档建议使用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
。看起来它在文档中被遗漏了。
更新:
path.exists
现在path.existsSync
已弃用。请使用。fs.exists
和fs.existsSync
2016 年更新:
fs.exists
并且 已被弃用。请改用fs.stat()或fs.access()。fs.existsSync
也
2019 年更新:
使用fs.existsSync
. 它没有被弃用。
https://nodejs.org/api/fs.html#fs_fs_existssync_path
使用当前推荐的(截至 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 或其他错误。
另一个更新
我自己需要这个问题的答案我查看了节点文档,似乎你不应该使用 fs.exists,而是使用 fs.open 并使用输出错误来检测文件是否不存在:
来自文档:
fs.exists() 是不合时宜的,仅出于历史原因而存在。几乎不应该有理由在您自己的代码中使用它。
特别是,在打开文件之前检查文件是否存在是一种反模式,这会使您容易受到竞争条件的影响:另一个进程可能会在调用 fs.exists() 和 fs.open() 之间删除文件。只需打开文件并在错误不存在时处理它。
我使用下面的函数来测试文件是否存在。它还捕获其他异常。因此,如果存在权限问题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 错误文档以防文件不存在:
{
[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
}
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);
}
这里的一些答案是这样说的,fs.exists
并且fs.existsSync
都被弃用了。根据文档,这不再是真的。只有fs.exists
现在被弃用:
请注意,不推荐使用 fs.exists(),但不推荐使用 fs.existsSync()。(fs.exists() 的回调参数接受与其他 Node.js 回调不一致的参数。fs.existsSync() 不使用回调。)
因此,您可以安全地使用fs.existsSync()来同步检查文件是否存在。
该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);
});
});
这已经得到解答,但是如果您喜欢安装可以使用的模块dtfe
,它代表
文件是否存在?
const dtfe = require('dtfe');
dtfe('package.json');
//=> true
您可以使用fs-extra (npm i fs-extra) 及其 fs.ensureFile 或目录 fs.ensureDir,因为 fs.exists 已被贬低,并且 fs.access 不建议您在使用后编辑该文件“不要在调用 fs.open()、fs.readFile() 或 fs.writeFile() 之前使用 fs.access() 检查文件的可访问性。这样做会引入竞争条件,因为其他进程可能会在“
为那些“正确”指出它并没有直接回答问题的人更新了答案,更多的是带来了另一种选择。
同步解决方案:
fs.existsSync('filePath')
另请参阅此处的文档。
如果路径存在则返回真,否则返回假。
异步承诺解决方案
在异步上下文中,您可以使用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');
}
}
如果您不打算操作文件,则说明使用该文件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;
}
❄️你可以使用graph-fs
directory.exists // boolean
这是一个简单的包装解决方案:
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() 似乎是最优雅的解决方案。
从答案看来,没有官方的 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 一样,让它变得毫无意义)。
很有可能,如果您想知道某个文件是否存在,您计划在存在时要求它。
function getFile(path){
try{
return require(path);
}catch(e){
return false;
}
}