node.js中fs.exists()即将被弃用,应该使用什么替代?

39
根据文档,node.js中的fs.exists()将被弃用。 他们的理由是:
fs.exists()已经过时,仅出于历史原因而存在。在自己的代码中几乎没有理由使用它。
特别是,在打开文件之前检查文件是否存在是一种反模式,会让您容易受到竞态条件的影响:另一个进程可能会在调用fs.exists()和fs.open()之间删除该文件。只需打开文件,当文件不存在时处理错误即可。
fs.exists()将被弃用。
我目前在移动文件之前使用它,因为fs.rename()似乎会悄悄地覆盖目标文件夹中同名的文件。
我的问题是:我应该使用什么来防止fs.rename()覆盖目标文件夹中的文件? 我认为有一种我不知道的方法。否则,我看不出fs.exists()被弃用的原因。
如建议所述,使用fs.open()似乎有些过度,因为我不想打开文件。

编辑,根据 @jfriend00 的要求提供更多关于我的工作的信息。

我正在制作一个 Electron 应用程序,用户可以将文件分类到不同的目录中。它不是服务器软件,而是旨在运行在日常用户的机器上,处理他们的文档。这是迄今为止移动文件的代码:

function moveFile(destIndex){
    var from = queue[currentQueueIndex].path;
    var to = destinations[destIndex].path + path.sep + path.basename(from);
    console.log("[i] Move file (from/to): ");
    console.log(from);
    console.log(to);

    //Check if file exists, if yes: give them the choice to cancel.
    fs.stat(to, function (err, stats) {
        if (err){
            move(from, to);
        } else {
            var confirmed = confirm("File already exists, will overwrite.");
            if (confirmed) {
                move(from, to);
            }
        }
    });
    next(); //Show the next file to the user
}

function move(from, to){
    fs.rename(from, to, function (err) {
        if (err) throw err;
        console.log('[i] Move successful');
        queue[currentQueueIndex].path = to;
        queue[currentQueueIndex].moved = true;
    });
}

在第一个评论中,从fs.stat开始的部分,我检查使用fs.rename创建的文件是否已经存在。我猜这可能会受到竞态条件的影响,但我找不到任何方式处理fs.rename中的重复项。
由于此应用程序旨在用于“家庭计算”,我认为在stat检查和重命名之间文件消失的情况不太可能发生。但是,仍然可以避免更多潜在的问题,这样做更好。

2
你究竟是想用 fs.exists() 做什么?在我们能够建议一些不容易出现竞态条件的替代方案之前,我们需要更多关于你代码序列的细节。问题在于,任何只检查文件是否存在然后基于此知识执行操作的替代方案都容易出现竞态条件,很可能有更好的编码方式。 - jfriend00
1
@jfriend00 - 我已经将我正在做的事情以及相关代码添加到主要问题中。 - Blargmode
1
在某种程度上是单用户计算机的最终用户提示的情况下,由此产生的竞争条件可能并不令人担忧。真正的答案将是fs.rename()的一个版本,它不会覆盖目标,因此您可以这样调用它,只有当重写失败,因为目标存在时,才提示并将参数更改为fs.rename()以允许覆盖。这将永远不会在未经提示的情况下进行覆盖。但是,我同意-在这种用例中可能不需要。 - jfriend00
5个回答

21

请使用 fs.existsSync()。

fs.existsSync() 没有被弃用。

https://nodejs.org/api/fs.html#fs_fs_existssync_path

fs.existsSync(path)

添加于: v0.1.21 path | 是 fs.exists() 的同步版本。如果文件存在,则返回 true;否则返回 false。

请注意,fs.exists() 已被弃用,但 fs.existsSync() 没有被弃用。(fs.exists() 的回调参数与其他 Node.js 回调不一致。fs.existsSync() 不使用回调。)


3
与其将函数弃用,不如只是更改函数接口以使之与其他 Node.js 回调函数保持一致,这似乎有些奇怪。 - user1694691
5
更改函数签名可能会破坏现有的代码。 - Eric Blade

17

io.js 文档 提到在一些情况下可使用 fs.stat()fs.access() 代替 fs.exists()


2
fs.stat() 看起来很有前途,但是当我尝试使用它 var stat = fs.statSync(file); 时,对于不存在的文件,我会得到一个 ENOENT 错误。我该如何处理这个问题? - Blargmode
1
我已经解决了。我将重命名函数放在stat函数内部,与此第四个示例相反,这使我能够使用stat而不是statSync。虽然if(error) doSomething();感觉有点奇怪。 - Blargmode
1
@Blargmode - 听起来你正在做的仍然受到竞态条件的影响。 - jfriend00
@jfriend00 从技术上讲是可以的,但 RENAME_NOREPLACE 标志仅在去年年初添加(https://lkml.org/lkml/2014/1/8/641),并且不适用于所有文件系统类型,而且仅适用于 Linux。此外,node/io.js没有提供 renameat2() 的绑定,这是您将传递该标志的系统调用。 - mscdex
我询问了原帖作者他们正在做什么,因为我认为我们可以为他们提供更好的建议来避免竞态条件,而不仅仅是使用一个替代 fs.exists() 的方法,该方法也容易受到相同竞态条件的影响。在这种情况下,如果原帖作者分享更多关于他们的代码和具体尝试做什么的信息,我认为有一个更好的答案比仅仅提供这个解决方法。 - jfriend00

11

这是一个使用 fs.access 的示例(在旧的 Node 版本中,使用 fs.stat 代替 fs.access),同时正确处理错误:

import { access } from 'node:fs/promises';

async function fileExists(filename) {
    try {
        await access(filename);
        return true;
    } catch (err) {
        if (err.code === 'ENOENT') {
            return false;
        } else {
            throw err;
        }
    }
}

这很整洁... - vighnesh153

4
这里是使用 fs.stat 的示例:-
fs.stat('mycustomfile.csv', function (err, stats) {
   console.log(stats);//here we got all information of file in stats variable
   if (err) {
       return console.error(err);
   }
     fs.unlink('mycustomfile.csv',function(err){
        if(err) return console.log(err);
        console.log('file deleted successfully');
   });  
});

也许我想要处理文件不存在的情况和其他错误发生的情况不同。我该怎么处理呢? - Ozymandias

4
const exists = !!(await fs.promises.stat(filename).catch(() => null))

2
谢谢你的那个一行代码,但是天哪,他们为什么要这样破坏标准模块呢? - F.H.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接