65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Node.js FS 异步等待文件更改

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2016 年 3 月 25 日

MIT

6分钟阅读

viewsIcon

24961

downloadIcon

189

在指定的时间跨度内异步等待文件和文件夹的创建、修改或删除。非常有帮助,例如,用于端到端测试或服务器应用程序。

引言

让我们假设以下场景。我们有一个网站或 Web 应用程序,用户可以在其中触发 UI 中的特定操作。此操作的结果将写入磁盘上的文件。现在,我们的工作是为该软件编写端到端测试(也称为 e2e 测试),最终使用像 Protractor [^] 这样的框架。

可能的使用场景包括

  • 具有文件系统访问权限的独立 Web 应用程序
  • 服务器客户端应用程序,其中服务器应将数据保存到文件
  • 服务器客户端应用程序,当客户端应下载文件时

被测软件会将给定数据写入文件系统上的文件(或不写)。我们的测试必须断言这是否按规定发生,并在规定的最长时间内发生。因此,我们需要一个等待给定文件出现的测试。当文件创建后,测试可以继续执行下一步,例如断言文件内容。如果在指定的时间跨度内文件未出现,则测试应失败。

概念

Sleep

最简单的方法是简单地暂停测试一段时间,同时应用程序正在创建文件。之后,测试可以检查文件是否存在并继续,或者以红色退出。但这种方法存在问题。一方面,等待时间应尽可能短,以使我们的测试更快。另一方面,此持续时间应足够长,以免在扩展应用程序逻辑或更改测试硬件时导致测试不稳定。

Wait

对于讨论的计时问题,我们需要一个更高级的解决方案,允许我们等待文件并在其创建时做出反应。如果我们有一个具有此 API 的库,编写 e2e 测试会容易得多

  • 在给定时间内监视一组文件。文件不必一开始就存在。
  • 如果文件在超时时间内发生更改:创建文件、修改文件(内容)或删除文件。
  • 如果文件在给定超时时间内未更改,则通知。

最后,由于我们要使用 Protractor 和 Jasmine,因此库 API 必须适合 Protractor 的异步调用模式。

编写代码

此代码是 Node.js fs 库的一个薄包装。watchFile 函数(参见 文档 [^])监视一个文件,并在每次文件更改时通知调用者。这包括文件的创建、修改和删除。我们所做的就是添加一个超时并将回调参数进行一点解释。

概述

API 将提供两个函数:一个用于开始等待文件更改,一个用于停止。在后台,我们有一个 private 函数来处理通知回调。就这样。

开始等待

让我们从 startWaiting 函数开始。此函数在给定时间内开始查找文件更改。它可以应用于文件、目录或 fs.watchFile 的任何其他有效目标。

使用 fs.watchFile,我们注册一个回调函数,在文件更改时调用。将 onChanged 回调包装在此匿名函数中,以传递客户端的回调以及一些局部变量。
我们使用 setTimeout 启动计时器。它将调用 stopWaiting 并用“Timed out.”消息通知客户端。我们将在 timer 中存储 setTimeout 的结果,以便稍后取消计时器。

function startWaiting(path, callback, timeout) {
    var timer = setTimeout(function () {
            stopWaiting(path);
            callback('Timed out.');
    }, timeout);

    fs.watchFile(path, options, function (curr, prev) {
        onChanged(curr, prev, path, timer, callback);
    });
}

停止等待

stopWaiting 函数通过调用 fs.unwatchFile 来结束对文件的监视,同时计时器仍在运行。它将目标文件的 path 作为单个参数,非常简单,如下所示:

function stopWaiting(path) {
    fs.unwatchFile(path, this);
}

响应文件更改

StartWaitingonChanged 注册为 fs.watchFile 的回调。此函数是本示例中最复杂的一个。因此,让我们在这里花一些时间。

识别更改事件

currentprevious 参数包含 fs.watchFile 的文件属性 stats [^]。比较这两个对象将帮助我们弄清楚实际发生了什么。mode(文件类型和模式)或 mtime(最后修改时间戳)属性似乎是最合适的(请参阅 man7.org [^] 以获得进一步解释)。

我们区分以下事件类型

  • 文件创建
  • 文件修改
  • 文件删除
  • 无文件

最后一个类型听起来可能有点令人惊讶,因为它实际上不是一个更改事件。当对不存在的文件或目录调用 fs.watchFile 时,API 会响应一个包含空对象的调用。是否通知我们的客户端取决于我们。

清理回调

对于 e2e 测试,我们只想对文件系统上的第一个事件做出反应。因此,我们使用 stopWaitingclearTimeout 清理回调注册(对于'无文件'事件,请跳过此步骤)。

通知调用者

此函数中的最后一步是通知原始调用者(也称为客户端)有关事件的信息。具体向客户端共享哪些详细信息取决于我们的 API 设计。我们可以将 API 限制为已识别的 type string,也可以传递文件stats

function onChanged(current, previous, path, timer, clientCallback) {
    var type = 'File modified.';
    if (current.mode === 0 && previous.mode === 0) type = 'No file.';
    else if (current.mode > 0 && previous.mode === 0) type = 'File created.';
    else if (current.mode === 0 && previous.mode > 0) type = 'File deleted.';

    if (type !== 'No file.') {
        stopWaiting(path);
        clearTimeout(timer);
    }

    clientCallback(type, current, previous);
}

监视选项

有一个小细节我们到目前为止还没有提到。fs.watchFile 函数接受一个 options 参数来定义轮询行为。在此演示中,我们将 interval 设置为 500 毫秒。

var options = {
    persistent: true,
    interval: 500
};

关注点

示例代码

要查看完整的示例,请下载附件。您可以直接运行小型的 Node.js 脚本,而无需 npm install。

$ node index.js

替代方案

fs.watch

Node.js 的 fs.watch 函数(此处记录 [^])允许我们监视目录或文件的更改。与 fs.watchFile 不同,此函数使用更高级的操作系统函数而不是轮询(例如,Linux 上的inotify和 Windows 上的 ReadDirectoryChangesW),这无疑是更优雅的解决方案。由于如果目标文件最初不存在,此函数会抛出错误,因此在 e2e 测试场景中监视特定文件很困难。一种选择是监视父目录。但是,然后我们必须在目录的每次更改事件上检查修改后的文件,以选择相关的。

fs.access

另一种方法可能是 fs.access文档 [^]),它检查当前应用程序是否可以访问该文件。在某些用例中,这些详细信息可能非常有帮助。对于此函数,我们必须手动实现拉取循环。我只建议在可访问性是关键标准时这样做。

结论

Node.js 的 fs 库不包含一个 API 函数来通知特定文件的创建。但是,只需稍加努力,我们就可以编写一个薄适配器来提供此行为。我们的库等待文件或目录被更改(创建、修改或删除)。此文件一开始不必存在。现在,我们的 e2e 测试可以消费此库。

如果您有任何评论、问题或改进建议,请随时在下方写下评论。

历史

  • 2016 年 3 月 25 日:初始版本
© . All rights reserved.