388. Java IO API - 处理事件
在事件处理循环中,事件的处理顺序通常如下:
- 获取 WatchKey
WatchService提供了三种方法来获取排队的WatchKey:- poll():如果有排队的
WatchKey,立即返回;如果没有,返回null。 - poll(long, TimeUnit):如果有排队的
WatchKey,立即返回。如果没有,程序会等待指定的时间,直到有事件发生。TimeUnit参数决定了等待时间的单位(如纳秒、毫秒等)。 - take():返回一个排队的
WatchKey。如果没有排队的WatchKey,则该方法会阻塞,直到有事件发生。
- poll():如果有排队的
- 处理 WatchKey 中的待处理事件
通过
WatchKey获取一个事件列表(pollEvents()方法返回WatchEvent对象列表)。每个WatchEvent对象表示一个文件系统事件。 - 检索事件类型
通过
WatchEvent的kind()方法,可以获取事件的类型。例如:文件创建、文件删除、文件修改等。请注意,即使你只注册了某些特定事件,OVERFLOW事件也可能会被触发,表示可能有事件丢失。你可以选择忽略该事件,或者进行适当的处理。 - 检索与事件相关的文件名
事件的上下文(context)包含了与事件相关的文件名。你可以通过
context()方法获取文件名。文件名通常是Path类型的对象。 - 重置 WatchKey
处理完事件后,需要通过调用
WatchKey的reset()方法将其重置到准备接收事件的状态。如果reset()返回false,说明该WatchKey不再有效,监控循环可以退出。必须注意,如果不调用reset(),WatchKey将无法接收进一步的事件。
WatchKey 的状态
WatchKey 在生命周期中会有三种可能的状态:
- Ready:表示该
WatchKey准备好接受事件,创建时默认为 Ready 状态。 - Signaled:表示一个或多个事件已经排队。当
WatchKey被信号唤醒后,它不再处于 Ready 状态,直到调用reset()方法。 - Invalid:表示
WatchKey不再有效。这种状态会发生在以下情况:- 程序显式地通过
cancel()方法取消了WatchKey。 - 监控的目录变得无法访问。
- 监视服务(WatchService)被关闭。
- 程序显式地通过
事件处理示例代码
以下是一个示例代码,展示了如何使用 WatchService 来监控目录中的文件事件,并处理新的文件。假设我们希望监控新文件的创建并验证该文件是否为文本文件:
for (;;) {
// 等待 WatchKey 被信号唤醒
WatchKey key;
try {
key = watcher.take(); // 阻塞直到有事件发生
} catch (InterruptedException x) {
return; // 如果线程被中断,退出循环
}
// 处理该 WatchKey 排队中的事件
for (WatchEvent<?> event: key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
// 如果是 OVERFLOW 事件,跳过处理
if (kind == OVERFLOW) {
continue;
}
// 获取文件名,即事件的上下文信息
WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path filename = ev.context();
// 验证文件是否为文本文件
try {
// 解析文件的完整路径,结合监控目录
Path child = dir.resolve(filename);
if (!Files.probeContentType(child).equals("text/plain")) {
System.err.format("新文件 '%s' 不是文本文件。%n", filename);
continue;
}
} catch (IOException x) {
System.err.println(x);
continue;
}
// 发送邮件处理文件(具体实现略)
System.out.format("正在发送文件 %s 到指定邮箱%n", filename);
// 发送邮件的细节交给读者实现...
}
// 重置 WatchKey,以便接收进一步的事件
boolean valid = key.reset();
if (!valid) {
break; // 如果 WatchKey 无效,退出循环
}
}
代码解释
key = watcher.take()这一行代码会阻塞程序,直到有事件发生。当监控的目录中发生变化时,WatchKey会被唤醒并返回。for (WatchEvent<?> event : key.pollEvents())处理排队中的所有事件。每个事件都是一个WatchEvent对象,你可以通过它来获取事件类型和文件名。- 事件类型判断
通过
event.kind()获取事件类型。如果事件是OVERFLOW,表示事件队列发生了溢出,我们通常跳过这种事件。它的出现意味着某些事件可能被丢失或者丢弃。 - 文件名获取
每个事件都有一个上下文(context),其中包含了与事件相关的文件路径。通过
event.context()获取文件名。然后,可以根据需要对该文件进行处理,比如验证文件类型或其他操作。 - 验证文件类型
使用
Files.probeContentType(child)来检查文件的内容类型。如果文件不是文本文件,就打印错误并跳过该文件。 - 发送邮件(简化示例) 这里是一个简化的逻辑,假设在文件验证通过后,程序会将该文件通过电子邮件发送出去。具体的电子邮件发送逻辑需要进一步补充。
- 重置 WatchKey
处理完事件后,通过
key.reset()重置WatchKey,以便继续监控其他事件。如果reset()返回false,表示WatchKey不再有效,监控循环将退出。
总结
这个事件处理的示例展示了如何通过 WatchService 监控文件系统的变化,并且根据事件的类型处理文件。通过合理地使用事件类型判断、文件类型验证和 WatchKey 重置,你可以实现一个健壮的文件变化监控系统。同时,需要注意 OVERFLOW 事件的处理,以确保不会错过任何重要的文件变化。