388. Java IO API - 处理事件

0 阅读4分钟

388. Java IO API - 处理事件

在事件处理循环中,事件的处理顺序通常如下:

  1. 获取 WatchKey WatchService 提供了三种方法来获取排队的 WatchKey
    • poll():如果有排队的 WatchKey,立即返回;如果没有,返回 null
    • poll(long, TimeUnit):如果有排队的 WatchKey,立即返回。如果没有,程序会等待指定的时间,直到有事件发生。TimeUnit 参数决定了等待时间的单位(如纳秒、毫秒等)。
    • take():返回一个排队的 WatchKey。如果没有排队的 WatchKey,则该方法会阻塞,直到有事件发生。
  2. 处理 WatchKey 中的待处理事件 通过 WatchKey 获取一个事件列表(pollEvents() 方法返回 WatchEvent 对象列表)。每个 WatchEvent 对象表示一个文件系统事件。
  3. 检索事件类型 通过 WatchEventkind() 方法,可以获取事件的类型。例如:文件创建、文件删除、文件修改等。请注意,即使你只注册了某些特定事件,OVERFLOW 事件也可能会被触发,表示可能有事件丢失。你可以选择忽略该事件,或者进行适当的处理。
  4. 检索与事件相关的文件名 事件的上下文(context)包含了与事件相关的文件名。你可以通过 context() 方法获取文件名。文件名通常是 Path 类型的对象。
  5. 重置 WatchKey 处理完事件后,需要通过调用 WatchKeyreset() 方法将其重置到准备接收事件的状态。如果 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 无效,退出循环
    }
}

代码解释

  1. key = watcher.take() 这一行代码会阻塞程序,直到有事件发生。当监控的目录中发生变化时,WatchKey 会被唤醒并返回。
  2. for (WatchEvent<?> event : key.pollEvents()) 处理排队中的所有事件。每个事件都是一个 WatchEvent 对象,你可以通过它来获取事件类型和文件名。
  3. 事件类型判断 通过 event.kind() 获取事件类型。如果事件是 OVERFLOW,表示事件队列发生了溢出,我们通常跳过这种事件。它的出现意味着某些事件可能被丢失或者丢弃。
  4. 文件名获取 每个事件都有一个上下文(context),其中包含了与事件相关的文件路径。通过 event.context() 获取文件名。然后,可以根据需要对该文件进行处理,比如验证文件类型或其他操作。
  5. 验证文件类型 使用 Files.probeContentType(child) 来检查文件的内容类型。如果文件不是文本文件,就打印错误并跳过该文件。
  6. 发送邮件(简化示例) 这里是一个简化的逻辑,假设在文件验证通过后,程序会将该文件通过电子邮件发送出去。具体的电子邮件发送逻辑需要进一步补充。
  7. 重置 WatchKey 处理完事件后,通过 key.reset() 重置 WatchKey,以便继续监控其他事件。如果 reset() 返回 false,表示 WatchKey 不再有效,监控循环将退出。

总结

这个事件处理的示例展示了如何通过 WatchService 监控文件系统的变化,并且根据事件的类型处理文件。通过合理地使用事件类型判断、文件类型验证和 WatchKey 重置,你可以实现一个健壮的文件变化监控系统。同时,需要注意 OVERFLOW 事件的处理,以确保不会错过任何重要的文件变化。