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





5.00/5 (4投票s)
在指定的时间跨度内异步等待文件和文件夹的创建、修改或删除。非常有帮助,例如,用于端到端测试或服务器应用程序。
引言
让我们假设以下场景。我们有一个网站或 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);
}
响应文件更改
StartWaiting
将 onChanged
注册为 fs.watchFile
的回调。此函数是本示例中最复杂的一个。因此,让我们在这里花一些时间。
识别更改事件
current
和 previous
参数包含 fs.watchFile
的文件属性 stats [^]。比较这两个对象将帮助我们弄清楚实际发生了什么。mode
(文件类型和模式)或 mtime
(最后修改时间戳)属性似乎是最合适的(请参阅 man7.org [^] 以获得进一步解释)。
我们区分以下事件类型
- 文件创建
- 文件修改
- 文件删除
- 无文件
最后一个类型听起来可能有点令人惊讶,因为它实际上不是一个更改事件。当对不存在的文件或目录调用 fs.watchFile
时,API 会响应一个包含空对象的调用。是否通知我们的客户端取决于我们。
清理回调
对于 e2e 测试,我们只想对文件系统上的第一个事件做出反应。因此,我们使用 stopWaiting
和 clearTimeout
清理回调注册(对于'无文件'事件,请跳过此步骤)。
通知调用者
此函数中的最后一步是通知原始调用者(也称为客户端)有关事件的信息。具体向客户端共享哪些详细信息取决于我们的 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 日:初始版本