如何为 fs.readFileSync() 捕获没有文件?

Posted

技术标签:

【中文标题】如何为 fs.readFileSync() 捕获没有文件?【英文标题】:How to capture no file for fs.readFileSync()? 【发布时间】:2013-01-01 17:40:27 【问题描述】:

在 node.js 中readFile() 显示了如何捕获错误,但是没有关于错误处理的readFileSync() 函数的注释。因此,如果我在没有文件的情况下尝试使用 readFileSync(),则会收到错误 Error: ENOENT, no such file or directory

如何捕获抛出的异常? doco 没有说明抛出了哪些异常,所以我不知道我需要捕获哪些异常。我应该注意,我不喜欢通用的“捕捉每一个可能的异常”风格的 try/catch 语句。在这种情况下,我希望捕获文件不存在时发生的特定异常,并尝试执行 readFileSync。

请注意,我仅在启动连接尝试之前执行同步功能,因此不需要我不应该使用同步功能的 cmets :-)

【问题讨论】:

你也可以使用fs.existsSync(),如my new answer所示 【参考方案1】:

基本上,fs.readFileSync 在找不到文件时会引发错误。此错误来自Error 原型并使用throw 抛出,因此捕获的唯一方法是使用try / catch 块:

var fileContents;
try 
  fileContents = fs.readFileSync('foo.bar');
 catch (err) 
  // Here you get the error when the file was not found,
  // but you also get any other error

很遗憾,您无法仅通过查看原型链来检测引发了哪个错误:

if (err instanceof Error)

是你能做的最好的,这对于大多数(如果不是全部)错误都是正确的。因此,我建议您使用 code 属性并检查其值:

if (err.code === 'ENOENT') 
  console.log('File not found!');
 else 
  throw err;

这样,您只处理这个特定错误并重新抛出所有其他错误。

或者,您也可以访问错误的message 属性来验证详细的错误消息,在本例中为:

ENOENT, no such file or directory 'foo.bar'

希望这会有所帮助。

【讨论】:

谢谢,这就是我要找的信息。我只是假设这将是一种特定类型的错误。我也刚刚意识到我误解了 try/catch 的工作原理,我认为您可以捕获特定的错误类型(a la java)。感谢您提供信息戈洛。 :-) 另外EACCES代码应该在if语句中检查文件存在但由于缺少权限而无法读取的情况【参考方案2】:

我更喜欢这种处理方式。您可以检查文件是否同步存在:

var file = 'info.json';
var content = '';

// Check that the file exists locally
if(!fs.existsSync(file)) 
  console.log("File not found");


// The file *does* exist
else 
  // Read the file and do anything you want
  content = fs.readFileSync(file, 'utf-8');

注意:如果您的程序还删除了文件,则这会产生竞争条件,如 cmets 中所述。但是,如果您只写入或覆盖文件而不删除它们,那么这完全没问题。

【讨论】:

现在,fs.existsSync 是 not deprecated anymore:“请注意 fs.exists() 已弃用,但 fs.existsSync() 不是。” 一点也不好。如果文件在 existsSync 和 readFileSync 调用之间从磁盘中删除怎么办?您的代码现在有一个内置的竞争条件等待发生... @tkarls 是的,完全正确,这是 2015 年我还在学习 Node.js 时写的,它有一个竞争条件。然而,有两点需要注意:这种竞争条件的可能性很小,基本上可以忽略,第二个和取代第一个是我现在将使用 try/catch 和 async/await 使我的代码更灵活“其他”异常(因为 Node 对异常友好)。 比赛条件不重要,直到他们这样做。像这样的答案就是为什么软件漏洞百出,为什么需要经常重启计算机,为什么有这么多安全漏洞等等。Stack Overflow 应该有一个标记可能有害的答案。 我想在这里插话并同意所涉及的两个人的观点。但不同意@tkarls 黑白描述。例如,我正在编写用于解析磁盘上的 markdown 文件的代码,这些文件永远不会被删除。在这种情况下,当我知道永远不会出现这种竞争条件时,检查它是否存在比使用 try catch 作为控制流更有意义。软件工程不是对与错,是所有情况下的事情......【参考方案3】:

您必须捕获错误,然后检查它是什么类型的错误。

try 
  var data = fs.readFileSync(...)
 catch (err) 
  // If the type is not what you want, then just throw the error again.
  if (err.code !== 'ENOENT') throw err;

  // Handle a file-not-found error

【讨论】:

... 让那个 'throw err;' 有没有办法用函数的非同步版本捕获同样的错误? @KiJéy 异步代码将错误作为回调的第一个参数传递,因此如果您检查是否会得到相同的行为。【参考方案4】:

我对这些场景使用立即调用的 lambda:

const config = (() => 
  try 
    return JSON.parse(fs.readFileSync('config.json'));
   catch (error) 
    return ;
  
)();

async版本:

const config = await (async () => 
  try 
    return JSON.parse(await fs.readFileAsync('config.json'));
   catch (error) 
    return ;
  
)();

【讨论】:

您可能希望在帖子中添加您的解决方案适用于 ECMAScript 6。截至 2018 年 1 月 1 日,IE 不支持大约 77% 的浏览器使用率 (caniuse.com/#feat=arrow-functions) .我很好奇,你们是如何迎合IE用户的? @Metalskin Webpack + Babel。但是,fs 是一个 Node 模块 啊,我和node脱节了,我问这个问题时怀疑node不支持ES6(可能是错的)。有点忘了这也是一个节点问题;-) 更新这个...fs.readFileAsync() 现在是fs.readFile() 并且也不应该将异步函数放在 node.js 中的 try/catch 中。 try/catch 永远不会得到错误,因为它是异步的。而是在回调中传递错误并在那里处理它:fs.readFile('/etc/passwd', (err, data) => if (err) throw err; console.log(data); ); 来自:nodejs.org/dist/latest-v12.x/docs/api/… 我相信如果promise被拒绝并且你正在等待promise,try-catch会被调用。【参考方案5】:

javascript 的 try...catch 机制不能用于拦截异步 API 生成的错误。初学者的一个常见错误是尝试在错误优先回调中使用 throw:

// THIS WILL NOT WORK:
const fs = require('fs');

try 
  fs.readFile('/some/file/that/does-not-exist', (err, data) => 
    // Mistaken assumption: throwing here...
    if (err) 
      throw err;
    
  );
 catch (err) 
  // This will not catch the throw!
  console.error(err);

这不起作用,因为传递给 fs.readFile() 的回调函数是异步调用的。当回调被调用时,周围的代码,包括 try...catch 块,将已经退出。在大多数情况下,在回调中抛出错误会使 Node.js 进程崩溃。如果启用了域,或者使用 process.on('uncaughtException') 注册了处理程序,则可以拦截此类错误。

参考: https://nodejs.org/api/errors.html

【讨论】:

【参考方案6】:

尝试使用 Async 来避免阻塞 NodeJS 的唯一线程。检查这个例子:

const util = require('util');
const fs = require('fs');
const path = require('path');
const readFileAsync = util.promisify(fs.readFile);

const readContentFile = async (filePath) => 
  // Eureka, you are using good code practices here!
  const content = await readFileAsync(path.join(__dirname, filePath), 
    encoding: 'utf8'
  )
  return content;

稍后可以将此异步函数与任何其他函数的 try/catch 一起使用:

const anyOtherFun = async () => 
  try 
    const fileContent = await readContentFile('my-file.txt');
   catch (err) 
    // Here you get the error when the file was not found,
    // but you also get any other error
  

编码愉快!

【讨论】:

Please note that I'm performing sync functions only on start up before serving connection attempts, so comments that I shouldn't be using sync functions are not required :-) ... 因此,请注意这种初始化逻辑的性能不佳,Observables/RxJS 是等待初始化而无需使用同步函数阻塞进程的不错选择

以上是关于如何为 fs.readFileSync() 捕获没有文件?的主要内容,如果未能解决你的问题,请参考以下文章

fs.readFileSync 不是 Meteor 的函数,React

Node.JS fs.readFileSync() 错误参数

使用 lambda 部署时 fs.readFileSync 找不到文件

fs.readFileSync无法读取nodejs中的文件传递路径变量

fs.readFileSync 不是文件相关的?节点.js

如何将使用 fs.readFileSync() 的 Node.js 代码重构为使用 fs.readFile()?