38
 fs.watch( 'example.xml', function ( curr, prev ) {
   // on file change we can read the new xml
   fs.readFile( 'example.xml','utf8', function ( err, data ) {
     if ( err ) throw err;
     console.dir(data);
     console.log('Done');
   });
 });

输出:

  • 一些数据
  • 完成 X 1
  • 一些数据
  • 完成 X 2

这是我的使用错误还是..?

4

15 回答 15

44

fs.watchapi :

  1. 不稳定_
  2. 已经知道关于重复通知的“行为” 。具体来说,windows 案例是 windows 设计的结果,其中单个文件修改可以是对 windows API 的多次调用
于 2012-10-19T17:53:24.600 回答
20

我通过执行以下操作来考虑这一点:

var fsTimeout

fs.watch('file.js', function(e) {

    if (!fsTimeout) {
        console.log('file.js %s event', e)
        fsTimeout = setTimeout(function() { fsTimeout=null }, 5000) // give 5 seconds for multiple events
    }
}
于 2013-09-15T03:38:54.853 回答
17

我建议使用chokidarhttps://github.com/paulmillr/chokidar),这比fs.watch

评论其 README.md:

节点.js fs.watch

  • 不报告 OS X 上的文件名。
  • 在 OS X 上使用 Sublime 等编辑器时根本不报告事件。
  • 经常报告事件两次。
  • 发出大多数更改为rename.
  • 还有很多其他问题
  • 不提供递归查看文件树的简单方法。

节点.js fs.watchFile

  • 在事件处理方面几乎一样糟糕。
  • 也不提供任何递归观看。
  • 导致 CPU 利用率高。
于 2015-10-09T22:07:40.890 回答
12

如果您需要查看文件的更改,那么您可以查看我的小型库on-file-change。它检查触发change事件之间的文件 sha1 哈希。

解释为什么我们有多个触发事件:

您可能会注意到在某些情况下,单个创建事件会生成多个由您的组件处理的 Created 事件。例如,如果您使用 FileSystemWatcher 组件监视目录中新文件的创建,然后使用记事本创建文件对其进行测试,即使只创建了一个文件,您也可能会看到生成了两个 Created 事件。这是因为记事本在写入过程中执行了多个文件系统操作。记事本分批写入磁盘,创建文件内容,然后创建文件属性。其他应用程序可以以相同的方式执行。因为 FileSystemWatcher 监视操作系统活动,所以这些应用程序触发的所有事件都将被拾取。

资源

于 2013-04-10T21:55:31.827 回答
4

我的自定义解决方案

我个人喜欢return在检查某些东西时防止代码块运行,所以,这是我的方法:

var watching = false;
fs.watch('./file.txt', () => {
    if(watching) return;
    watching = true;

    // do something

    // the timeout is to prevent the script to run twice with short functions
    // the delay can be longer to disable the function for a set time
    setTimeout(() => {
        watching = false;
    }, 100);
};

随意使用此示例来简化您的代码。它可能并不比使用其他模块更好,但效果很好!

于 2017-11-26T23:48:38.180 回答
1

我是第一次处理这个问题,所以到目前为止所有的答案可能都比我的解决方案更好,但是它们都不是 100% 适合我的情况,所以我想出了一些稍微不同的东西——我使用了 XOR翻转 0 和 1 之间的整数的操作,有效地跟踪和忽略文件上的每一秒事件:

var targetFile = "./watchThis.txt"; 
var flippyBit = 0; 

fs.watch(targetFile, {persistent: true}, function(event, filename) {

      if (event == 'change'){
        if (!flippyBit) {
          var data = fs.readFile(targetFile, "utf8", function(error, data) {
            gotUpdate(data);
          })
        } else {
          console.log("Doing nothing thanks to flippybit.");              
        }
        flipBit(); // call flipBit() function
      }
    });

// Whatever we want to do when we see a change
function gotUpdate(data) {
    console.log("Got some fresh data:");
    console.log(data);
    }


// Toggling this gives us the "every second update" functionality

function flipBit() {
    flippyBit = flippyBit ^ 1;
}

我不想使用与时间相关的函数(如 jwymanm 的答案),因为我正在观看的文件可能会非常频繁地获得合法更新。而且我不想使用 Erik P 建议的观看文件列表,因为我只看一个文件。Jan Święcki 的解决方案似乎有点矫枉过正,因为我正在低功耗环境中处理极短且简单的文件。最后,Bernado 的回答让我有点紧张——如果它在我处理完第一个更新之前到达,它只会忽略第二个更新,我无法处理那种不确定性。如果有人发现自己处于这种非常具体的场景中,我使用的方法可能有一些优点吗?如果它有任何严重问题,请让我知道/编辑这个答案,但到目前为止它似乎运作良好?

注意:显然,这强烈假设每次实际更改您将获得 2 个事件。显然,我仔细测试了这个假设,并了解了它的局限性。到目前为止,我已经确认:

  • 在 Atom 编辑器中修改文件并保存触发器2 更新
  • touch触发2 次更新
  • >通过(覆盖文件内容)的输出重定向触发2 次更新
  • 附加 via>> 有时会触发 1 个更新!*

我可以为不同的行为想出完美的理由,但我们不需要知道为什么会发生某些事情来为它做计划——我只是想强调你会想在你自己的环境和上下文中检查自己你自己的用例(duh),不要相信互联网上自认的白痴。话虽这么说,但采取了预防措施,到目前为止我还没有任何奇怪的事情发生。

*完全公开,我实际上不知道为什么会这样,但是我们已经在处理 watch() 函数的不可预测的行为,那么还有什么不确定性呢?对于在家跟随的任何人,更快速地附加到文件似乎会导致它停止双重更新,但老实说,我真的不知道,而且我对这个解决方案在实际情况下的行为感到满意它会被使用,这是一个单行文件,最快每秒会更新(替换内容)两次。

于 2015-09-26T06:36:33.637 回答
0

第一个是更改,第二个是重命名

我们可以从监听器函数中有所作为

function(event, filename) {

}

侦听器回调获取两个参数(事件、文件名)。event 是“rename”或“change”,filename 是触发事件的文件的名称。

// rm sourcefile targetfile
fs.watch( sourcefile_dir , function(event, targetfile)){
    console.log( targetfile, 'is', event)
}

由于源文件被重命名为目标文件,它将调用三个事件作为事实

null is rename // sourcefile not exist again
targetfile is rename
targetfile is change

请注意,如果您想捕获所有这三个 evnet,请查看源文件的目录

于 2013-01-21T02:26:25.507 回答
0

我有时会多次注册 Watch 事件,导致 Watch 事件触发多次。我通过保留观看文件列表来解决它,如果文件已经在列表中,则避免注册事件:

 var watchfiles = {};

function initwatch(fn, callback) {
    if watchlist[fn] {
        watchlist[fn] = true;
        fs.watch(fn).on('change', callback);
    }
}

……

于 2014-01-23T22:15:47.373 回答
0

类似/相同的问题。当图像被添加到目录时,我需要对它们做一些事情。这是我处理双重射击的方法:

var fs = require('fs');
var working = false;

fs.watch('directory', function (event, filename) {
  if (filename && event == 'change' && active == false) {
    active = true;

    //do stuff to the new file added

    active = false;
});

它将忽略第二次触发,直到完成与新文件有关的操作。

于 2015-07-13T03:23:50.820 回答
0

就像其他人的回答说...这有很多麻烦,但我可以这样处理:

var folder = "/folder/path/";

var active = true; // flag control

fs.watch(folder, function (event, filename) {
    if(event === 'rename' && active) { //you can remove this "check" event
        active = false;

        // ... its just an example
        for (var i = 0; i < 100; i++) {
            console.log(i);
        }

        // ... other stuffs and delete the file
        if(!active){
            try {
                fs.unlinkSync(folder + filename);
            } catch(err) {
                console.log(err);
            }
            active = true
        }
    }
});

希望我能帮助你...

于 2016-02-04T14:18:14.290 回答
0

最简单的解决方案:

const watch = (path, opt, fn) => {
  var lock = false
  fs.watch(path, opt, function () {
    if (!lock) {
      lock = true
      fn()
      setTimeout(() => lock = false, 1000)
    }
  })
}
watch('/path', { interval: 500 }, function () {
  // ...
})
于 2017-11-25T00:02:00.660 回答
0

我正在使用 puppeteer 下载文件,保存文件后,我正在发送自动电子邮件。由于上述问题,我注意到,我正在发送 2 封电子邮件。我通过使用process.exit()pm2 停止我的应用程序并自动启动来解决。在代码中使用标志并没有救我。

如果将来有人遇到此问题,也可以使用此解决方案。退出程序并自动使用监控工具重新启动。

于 2020-03-09T10:11:58.887 回答
0

我遇到了同样的问题。如果不想触发多次,可以使用去抖功能。

 fs.watch( 'example.xml', _.debounce(function ( curr, prev ) {
   // on file change we can read the new xml
   fs.readFile( 'example.xml','utf8', function ( err, data ) {
     if ( err ) throw err;
     console.dir(data);
     console.log('Done');
   });
 }, 100));
于 2021-12-12T15:43:16.737 回答
0

去抖观察者

我得出的一个解决方案是(a)需要有一个解决问题的方法,(b)需要有一个解决方案来确保多个快速Ctrl+s操作不会导致竞争条件。这就是我所拥有的...

./**/utilities.js(某处)
export default {
    ...
    debounce(fn, delay) {  // #thxRemySharp https://remysharp.com/2010/07/21/throttling-function-calls/
        var timer = null;
        
        return function execute(...args) {
            var context = this;
            clearTimeout(timer);
            timer = setTimeout(fn.bind(context, ...args), delay);
        };
    },
    ...
};
./**/file.js(别处)
import utilities from './**/utilities.js';  // somewhere
...
function watch(server) {
    const debounced = utilities.debounce(observeFilesystem.bind(this, server), 1000 * 0.25);
    const observers = new Set()
        .add( fs.watch('./src', debounced) )
        .add( fs.watch('./index.html', debounced) )
        ;
    console.log(`watching... (${observers.size})`);
    
    return observers;
}
    
function observeFilesystem(server, type, filename) {
    if (!filename) console.warn(`Tranfer Dev Therver: filesystem observation made without filename for type ${type}`);
    console.log(`Filesystem event occurred:`, type, filename);
    server.close(handleClose);
}
...

这样,我们传入的观察处理程序fs.watch是 [在这种情况下是一个绑定的 bunction],如果多个调用彼此相隔不到几秒(250 毫秒),它就会被去抖动。1000 * 0.25

值得注意的是,我还设计了一个Promises 管道来帮助避免其他类型的竞争条件,因为代码还利用了其他回调。另请注意 Remy Sharp 的贡献,其去抖动功能多年来一再证明非常有用。

于 2022-02-24T03:45:29.037 回答
0

这是我的简单解决方案。每次都运行良好。

// Update obj as file updates
obj = JSON.parse(fs.readFileSync('./file.json', 'utf-8'));
fs.watch('./file.json', () => {
  const data = JSON.parse(fs.readFileSync('./file.json', 'utf-8') || '{}');
  if(Object.entries(data).length > 0) { // This checks fs.watch() isn't false-firing
    obj = data;
    console.log('File actually changed: ', obj)
  }
});
于 2021-02-09T02:53:12.557 回答