Получение пустой строки из fs.readFile внутри chokidar.watch (path_file) .on ('change', ...) - PullRequest
1 голос
/ 29 февраля 2020

У меня есть следующий очень простой Node проект:

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

enter image description here

$ 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 -> Bill Gates ABC
  • модификация 2: Bill Gates ABC -> Bill Gates ABC DEF

работает нормально, выводит содержимое этого файла на консоль.

Но когда я делаю следующую модификацию:

  • модификация 3: Bill Gates ABC -> 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 ge в какой-то момент он заблокирован, и когда Node пытается прочитать его, он возвращает пустую строку, потому что он заблокирован.

Есть идеи о том, как заставить эту работу работать без сбоев после нескольких попыток?

Спасибо!

Ответы [ 2 ]

0 голосов
/ 01 марта 2020

Я мог бы заставить его работать, добавив некоторый запасной вариант восстановления:

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 события.

0 голосов
/ 29 февраля 2020

Это может быть состояние гонки. Сделайте ваш 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;
    }
  });

});
...