【问题标题】:Getting empty string from fs.readFile inside chokidar.watch(path_file).on('change', ...)从 chokidar.watch(path_file).on('change', ...) 中的 fs.readFile 获取空字符串
【发布时间】:2025-11-23 12:20:07
【问题描述】:

我有以下非常简单的Node 项目:

https://github.com/tlg-265/chokidar-issue

$ git clone https://github.com/tlg-265/chokidar-issue
$ cd chokidar-issue
$ npm i
$ npm run watch-changes

它主要负责检测文件上的更改:

/profiles/bill-gates.json

然后做一个动作。

为了做到这一点,我有以下文件:

/profile-watcher.js

const fs = require('fs-extra');
const colors = require('colors/safe');
const chokidar = require('chokidar');

const path_file = `profiles/bill-gates.json`;
console.log(`Current Profile: ${colors.red.bgBrightYellow(path_file)}`);

let profile_before = {};

chokidar.watch(path_file).on('change', async (path) => {

  console.log();
  console.log(`${colors.blue.bgYellow(`->`)} Profile changed: ${path}`);

  fs.readFile(path, (err, profile_json) => {
    console.log(`->${profile_json}<-`);
    let profile = JSON.parse(profile_json);
    if (JSON.stringify(profile) != JSON.stringify(profile_before)) {
      console.log('The profile has changed.');
      profile_before = profile;
    }
  });

});

当我运行项目时:

$ npm run watch-changes

并在文件中进行以下修改:/profiles/bill-gates.json

  • 修改1:Bill Gates -&gt; Bill Gates ABC
  • 修改2:Bill Gates ABC -&gt; Bill Gates ABC DEF

它工作正常,将这个文件的内容输出到控制台。

但是当我进行下一次修改时:

  • 修改3:Bill Gates ABC -&gt; Bill Gates ABC DEF GHI

然后我得到以下错误:

-> Profile changed: profiles\bill-gates.json
-><-
undefined:1

SyntaxError: Unexpected end of JSON input
    at JSON.parse (<anonymous>)
    at fs.readFile (\chokidar-issue\profile-watcher.js:17:24)
    at \chokidar-issue\node_modules\graceful-fs\graceful-fs.js:115:16
    at FSReqWrap.readFileAfterClose [as oncomplete] (internal/fs/read_file_context.js:53:3)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! chokidar-issue@1.0.0 watch-changes: `node profile-watcher.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the chokidar-issue@1.0.0 watch-changes script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Roaming\npm-cache\_logs\2020-02-28T23_44_01_038Z-debug.log

/profiles/bill-gates.json(标志:UTF-8 / CRLF

{
  "name": "Bill Gates",
  "email": "bill.gates@microsoft.com",
  "password": "windows",
  "country": "USA"
}

顺便说一句,如果我从CRLF 更改为LF,通常我可以在它崩溃之前再做一些修改。

我的印象是,由于某种原因,文件:/profiles/bill-gates.json 在某些时候被锁定,当 Node 尝试读取它时,它返回一个空字符串,因为它已被锁定。

关于如何在几次尝试后不崩溃的情况下完成这项工作的任何想法?

谢谢!

【问题讨论】:

  • 可以发/profiles/bill-gates.json的内容吗?首先,删除其中的所有内容,然后重新运行,然后发布内容。

标签: javascript node.js npm fs-extra chokidar


【解决方案1】:

我和你有同样的问题。

“chokidar”中有一个选项,您可以在其中awaitWriteFinish。它是基于时间的,并检查文件的大小是否在变化。如果没有,那么它会调用回调。

const watcher = chokidar.watch(configPathString, { 
    persistent: true,
    awaitWriteFinish: {
        stabilityThreshold: 500
    } 
});
watcher.on('change', (path, stats) => {

    fs.readFile(configPathString,(err, data)=>{
        if (err) throw err;
        
        //console.log('data',data);

        let receivedData = JSON.parse(data);

        //Do whatever you like
    })
});

【讨论】:

    【解决方案2】:

    这可能是一个竞争条件。像这样让您的 JSON.parse 安全:

    const path = require('path')
    
    chokidar.watch(path_file).on('change', async (path) => {
      fs.readFile(path, 'utf8', (err, profile_json) => {
        if (!profile_json) {
          console.log(`${path} is an empty file!`)
          return
        }
        const profile = JSON.parse(profile_json);
        if (JSON.stringify(profile) != JSON.stringify(profile_before)) {
          console.log('The profile has changed.');
          profile_before = profile;
        }
      });
    
    });
    

    【讨论】:

    • 我刚刚添加了utf8 标志,问题仍然存在。我还添加了您的额外调试日志(更新了存储库),它说路径存在。另外,请记住,我上面提到的前 2 次可以正常工作,但第三次不能正常工作。那么为什么前两次路径应该没问题,但第三次就不行了呢?
    • 无法使用您的存储库进行复制。 System Version: macOS 10.15.3 (19D76) Kernel Version: Darwin 19.3.0
    【解决方案3】:

    我可以通过添加一些恢复后备来使其工作:

    const fs = require('fs-extra');
    const colors = require('colors/safe');
    const chokidar = require('chokidar');
    const sleep = require('sleep');
    
    const path_file = `profiles/bill-gates.json`;
    console.log(`Current Profile: ${colors.red.bgBrightYellow(path_file)}`);
    
    let profile_before = fs.readFileSync(path_file).toString();
    
    chokidar.watch(path_file).on('change', async (path_changed) => {
      let profile = fs.readFileSync(path_changed).toString();
      if (IsValidJson(profile)) {
        if (profile != profile_before) {
          console.log();
          console.log(`Profile changed: ${colors.red.bgBrightYellow(path_changed)}`);
          process_profile(profile);
          profile_before = profile;
        }
      }
      else {
        sleep.msleep(100); // this is necessary
      }
    });
    
    function process_profile(profile_json) {
      const profile = JSON.parse(profile_json);
      console.log(`${profile_json}`);
      console.log(profile.name);
    }
    
    function IsValidJson(str) {
      try {
        JSON.parse(str);
      } catch (e) {
        return false;
      }
      return true;
    }
    

    似乎当您保存文件时(至少在 Windows 上),有时在文件变得清晰和几毫秒后它得到实际内容之间有一段时间(非常短的时间)。在这两种情况下,on-change 事件都会被触发。因此,我们只需要验证文件的内容是否为 JSON。在这种情况下,我只需要忽略它并等待下一个on-change 事件。

    【讨论】: