前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >NodeJS是如何监听文件的变化?

NodeJS是如何监听文件的变化?

作者头像
心谭博客
发布2020-04-21 15:15:16
4.4K0
发布2020-04-21 15:15:16
举报
文章被收录于专栏:YuanXinYuanXin

Keywords: 操作系统差异、识别用户/编辑器操作、连续触发的优化、工程级 API。

概述

NodeJS 提供了 fs.watch / fs.watchFile 两种 API:

  • fs.watch: 推荐,可以监听文件夹。基于操作系统。
  • fs.watchFile: 只能监听指定文件。并且通过轮询检测文件变化,不能响应实时反馈。

一个监听指定文件夹的代码如下:

代码语言:javascript
复制
fs.watch(dir, { recursive: true }, (eventType, file) => {
    if (file && eventType === "change") {
        console.log(`${file} 已经改变`);
    }
});

跨平台优化

对于不同系统内核,比如 maxos,fs.watch 回调函数中的第一个参数,不会监听到 rename、delete 事件。因此,这不是一个工程级别的可用 api

文件 md5

某些开源软件,会将文件内容都清空后,再添加内容。而且保存过程中,可能会出现多个中间态。

对于文件更改的情况,检测内容的 md5 值,是个不错的方法。

代码语言:javascript
复制
let previousMD5 = "";
fs.watch("./whatever", (type, filename) => {
    if (!filename) {
        return;
    }

    const md5 = crypto.createHash("md5");
    const currentMD5 = md5
        .update(fs.readFileSync(filename).toString())
        .digest("hex");
    if (currentMD5 === previousMD5) {
        return;
    }

    previousMD5 = currentMD5;
    console.log(`${filename} is changed`);
});

事件频率控制

对于文件变更,不同的系统可能会触发多个不同的中间态。因此,借助 debounce 函数的思想,控制和修正回调事件的触发频率。

前面的代码修正为:

代码语言:javascript
复制
let previousMD5 = "";
let watchWait = false; //

fs.watch("./whatever", (type, filename) => {
    if (!filename || watchWait) {
        return;
    }

    //
    watchWait = setTimeout(() => {
        watchWait = false;
    }, 100);

    const md5 = crypto.createHash("md5");
    const currentMD5 = md5
        .update(fs.readFileSync(filename).toString())
        .digest("hex");
    if (currentMD5 === previousMD5) {
        return;
    }

    previousMD5 = currentMD5;
    console.log(`${filename} is changed`);
});

文件信息

对于常见的库来说,除了不信任原生 API、使用上述技巧外,很重要的是,都根据 fs.Stats 类的信息,自定义逻辑来判断文件状态,以此保证不同平台兼容性

下面是在 Node10 中,打印的文件状态信息:

代码语言:javascript
复制
Stats {
  dev: 16777222,
  mode: 33188,
  nlink: 1,
  uid: 501,
  gid: 20,
  rdev: 0,
  blksize: 4096,
  ino: 6493141,
  size: 7,
  blocks: 8,
  atimeMs: 1567516873292.676,
  mtimeMs: 1567516873293.3867,
  ctimeMs: 1567516873293.3867,
  birthtimeMs: 1566547653640.1763,
  atime: 2019-09-03T13:21:13.293Z,
  mtime: 2019-09-03T13:21:13.293Z,
  ctime: 2019-09-03T13:21:13.293Z,
  birthtime: 2019-08-23T08:07:33.640Z }

通过文件信息的思路,就是在fs.stat()的回调函数中,进行逻辑处理:

代码语言:javascript
复制
// 判断文件是否写入完毕的操作
function awaitWriteFinish() {
    // ...省略
    fs.stat(
        fullPath,
        function(err, curStat) {
            // ...省略

            if (prevStat && curStat.size != prevStat.size) {
                this._pendingWrites[path].lastChange = now;
            }

            if (now - this._pendingWrites[path].lastChange >= threshold) {
                delete this._pendingWrites[path];
                awfEmit(null, curStat);
            } else {
                timeoutHandler = setTimeout(
                    awaitWriteFinish.bind(this, curStat),
                    this.options.awaitWriteFinish.pollInterval
                );
            }
        }.bind(this)
    );
    // ...省略
}

成熟的库

参考链接

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-09-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 跨平台优化
    • 文件 md5
      • 事件频率控制
        • 文件信息
        • 成熟的库
        • 参考链接
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档