2020-10-11 18:26:05
在Java中,使用WatchService API(Java NIO.2)是实时监控文件变化的推荐方案,其核心步骤如下:
1. 核心实现步骤创建WatchService实例通过FileSystems.getDefault().newWatchService()获取服务,用于接收文件系统事件。
WatchService watcher = FileSystems.getDefault().newWatchService();注册监控目录及事件类型指定需监控的目录(如./monitor_dir)和事件类型(ENTRY_CREATE、ENTRY_DELETE、ENTRY_MODIFY)。
Path dir = Paths.get("./monitor_dir");if (!Files.exists(dir)) { Files.createDirectories(dir);}dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);循环获取并处理事件使用take()阻塞等待事件,或poll()设置超时时间。遍历事件时需处理OVERFLOW(事件丢失)情况,并解析文件路径。
while (true) { WatchKey key; try { key = watcher.take(); // 阻塞等待事件 } catch (InterruptedException e) { break; } for (WatchEvent<?> event : key.pollEvents()) { WatchEvent.Kind<?> kind = event.kind(); if (kind == StandardWatchEventKinds.OVERFLOW) { System.err.println("事件溢出,可能丢失部分事件!"); continue; } Path filename = ((WatchEvent<Path>) event).context(); Path child = dir.resolve(filename); System.out.println(kind.name() + ": " + child); } boolean valid = key.reset(); // 重置Key以继续监听 if (!valid) break;}关闭资源监控结束后调用watcher.close()释放系统资源。
事件合并与重复操作系统或编辑器可能触发多个ENTRY_MODIFY事件(如临时文件替换)。需通过防抖机制(如ScheduledExecutorService延迟处理)减少重复操作。
// 示例:延迟1秒处理,若新事件到达则重置定时器ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);scheduler.schedule(() -> { // 实际处理逻辑}, 1, TimeUnit.SECONDS);子目录递归监控WatchService默认不监控子目录。需手动遍历子目录并逐个注册,同时处理新增子目录的动态注册。
资源管理确保调用watcher.close()避免资源泄漏,类似IO流的释放。
平台差异不同操作系统对符号链接、事件触发及时性的支持可能不同,需在测试阶段验证。
传统轮询(Polling)通过定时任务扫描目录,比较文件修改时间或哈希值。适用于对实时性要求低或不支持WatchService的环境,但效率较低。
Apache Commons IO使用FileAlterationMonitor简化监控,内部封装了WatchService或轮询机制,支持递归监控和事件批处理。
// 示例:通过Commons IO监控目录FileAlterationObserver observer = new FileAlterationObserver(dir.toString());observer.addListener(new FileAlterationListenerAdaptor() { @Override public void onFileCreate(File file) { System.out.println("文件创建: " + file); }});FileAlterationMonitor monitor = new FileAlterationMonitor(1000); // 1秒检查一次monitor.addObserver(observer);monitor.start();事件过滤在事件处理循环中,根据文件扩展名(如.log)或路径过滤无关事件。
Path filename = ((WatchEvent<Path>) event).context();if (filename.toString().endsWith(".tmp")) { continue; // 忽略临时文件}异步处理将事件处理逻辑移至独立线程或消息队列(如Kafka),避免阻塞WatchService主循环。
