Java WatchService监控指定路径下的文件新增、删除和修改(子文件夹、指定文件类型)

WatchService 是 Java NIO 包 (java.nio.file) 中提供的一个用于监控文件系统变化的 API。它允许应用程序监听目录中的文件创建、修改和删除事件。

基本原理

WatchService 使用操作系统提供的文件系统通知机制:

  • Windows: 使用 ReadDirectoryChangesW

  • Linux: 使用 inotify

  • Mac OS X: 使用 FSEvents

WatchService 和定时任务轮询是两种不同的文件监控策略,它们在资源消耗方面有显著差异。

资源消耗对比

特性 WatchService 定时任务轮询
工作机制 基于操作系统事件通知 定期主动扫描文件系统
CPU使用 低(事件驱动,空闲时几乎不消耗CPU) 高(每次扫描都需要CPU计算)
内存使用 中等(维护事件队列和状态) 取决于扫描范围和频率
I/O操作 极少(仅在有变化时触发) 高(每次扫描都需要读取文件系统)
响应延迟 近实时(毫秒级) 取决于轮询间隔(秒级或更长)
可扩展性 好(可监控大量文件) 差(大量文件时性能下降明显)

直接上实现代码:

复制代码
import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.*;

/*
    监控指定目录下文件的创建、删除和修改
 */
public class DirectoryWatcher {
    public static void main(String[] args) throws Exception {
        // 1. 创建WatchService实例
        WatchService watcher = FileSystems.getDefault().newWatchService();

        // 2. 注册要监控的目录
        Path dir = Paths.get("D:/2"); // 替换为你要监控的目录
        WatchKey key = dir.register(watcher,
                ENTRY_CREATE,
                ENTRY_DELETE,
                ENTRY_MODIFY);

        System.out.println("开始监控: " + dir);

        // 3. 事件处理循环
        while (true) {
            // 等待并获取下一个WatchKey
            key = watcher.take();

            // 处理所有事件
            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();

                System.out.printf("事件类型: %s, 文件: %s%n", kind.name(), filename);

                // 这里可以添加自定义的业务处理逻辑
                if (kind == ENTRY_CREATE) {
                    System.out.println("新文件创建: " + filename);
                } else if (kind == ENTRY_DELETE) {
                    System.out.println("文件删除: " + filename);
                } else if (kind == ENTRY_MODIFY) {
                    System.out.println("文件修改: " + filename);
                }
            }

            // 4. 重置WatchKey以继续接收事件
            boolean valid = key.reset();
            if (!valid) {
                System.out.println("WatchKey不再有效");
                break;
            }
        }
    }
}

运行效果:

开始监控: D:\2

事件类型: ENTRY_DELETE, 文件: _2025-03-17_13-26-32

文件删除: _2025-03-17_13-26-32

事件类型: ENTRY_CREATE, 文件: 新建文本文档.txt

新文件创建: 新建文本文档.txt

事件类型: ENTRY_DELETE, 文件: 新建文本文档.txt

文件删除: 新建文本文档.txt

事件类型: ENTRY_CREATE, 文件: 1.txt

新文件创建: 1.txt

复制代码
import java.nio.file.*;

import static java.nio.file.StandardWatchEventKinds.*;

/*
    监控指定目录下文件的创建、删除和修改
 */
import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.*;
import java.io.IOException;

/*
    监控指定目录(包含子文件夹)下文件的创建、删除和修改
 */
public class RecursiveDirectoryWatcher {
    private final WatchService watcher;
    private final Path rootDir;

    public RecursiveDirectoryWatcher(Path dir) throws IOException {
        this.watcher = FileSystems.getDefault().newWatchService();
        this.rootDir = dir;

        // 递归注册所有子目录
        registerAllSubdirectories(dir);
    }

    private void registerAllSubdirectories(final Path start) throws IOException {
        Files.walk(start)
                .filter(Files::isDirectory)
                .forEach(subDir -> {
                    try {
                        System.out.println("注册监控目录: " + subDir);
                        subDir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
                    } catch (IOException e) {
                        System.err.println("无法注册目录 " + subDir + ": " + e);
                    }
                });
    }

    public void startWatching() {
        System.out.println("开始监控目录树: " + rootDir);

        while (true) {
            WatchKey key;
            try {
                key = watcher.take();
            } catch (InterruptedException e) {
                System.err.println("监控被中断");
                return;
            }

            for (WatchEvent<?> event : key.pollEvents()) {
                WatchEvent.Kind<?> kind = event.kind();

                if (kind == OVERFLOW) {
                    continue;
                }

                @SuppressWarnings("unchecked")
                WatchEvent<Path> ev = (WatchEvent<Path>) event;
                Path filename = ev.context();
                Path dir = (Path) key.watchable();
                Path fullPath = dir.resolve(filename);

                System.out.printf("事件类型: %s, 文件: %s%n", kind.name(), fullPath);

                // 如果是新建目录,则注册监控
                if (kind == ENTRY_CREATE && Files.isDirectory(fullPath)) {
                    try {
                        registerAllSubdirectories(fullPath);
                    } catch (IOException e) {
                        System.err.println("无法注册新目录 " + fullPath + ": " + e);
                    }
                }
            }

            if (!key.reset()) {
                System.out.println("WatchKey不再有效");
                break;
            }
        }
    }

    public static void main(String[] args) throws IOException {
        Path dir = Paths.get("D:/2"); // 替换为你要监控的目录
        new RecursiveDirectoryWatcher(dir).startWatching();
    }
}
复制代码
import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.*;
import java.io.IOException;

/*
    监控指定目录(指定类型文件)文件的创建、删除和修改
 */
public class TxtFileWatcher {
    private final WatchService watcher;
    private final Path dir;

    public TxtFileWatcher(Path dir) throws IOException {
        this.watcher = FileSystems.getDefault().newWatchService();
        this.dir = dir;

        // 注册监控目录
        dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
    }

    public void startWatching() {
        System.out.println("开始监控.txt文件变化,目录: " + dir);

        while (true) {
            WatchKey key;
            try {
                key = watcher.take();
            } catch (InterruptedException e) {
                System.err.println("监控被中断");
                return;
            }

            for (WatchEvent<?> event : key.pollEvents()) {
                WatchEvent.Kind<?> kind = event.kind();

                if (kind == OVERFLOW) {
                    continue;
                }

                @SuppressWarnings("unchecked")
                WatchEvent<Path> ev = (WatchEvent<Path>) event;
                Path filename = ev.context();

                // 只处理.txt文件
                if (filename.toString().endsWith(".txt")) {
                    Path fullPath = dir.resolve(filename);

                    System.out.printf("事件类型: %s, 文件: %s%n", kind.name(), fullPath);

                    // 这里可以添加对.txt文件的特定处理逻辑
                    if (kind == ENTRY_CREATE) {
                        System.out.println("新的.txt文件创建: " + fullPath);
                    } else if (kind == ENTRY_MODIFY) {
                        System.out.println("txt文件被修改: " + fullPath);
                        // 可以在这里读取文件内容等操作
                    } else if (kind == ENTRY_DELETE) {
                        System.out.println("txt文件被删除: " + fullPath);
                    }
                }
            }

            if (!key.reset()) {
                System.out.println("WatchKey不再有效");
                break;
            }
        }
    }

    public static void main(String[] args) throws IOException {
        Path dir = Paths.get("D:/2"); // 替换为你要监控的目录
        new TxtFileWatcher(dir).startWatching();
    }
}
相关推荐
YDS8291 小时前
黑马点评 —— 分布式锁详解加源码剖析
java·spring boot·redis·分布式
迷藏4942 小时前
**发散创新:基于 Rust的开源权限管理系统设计与实战**在现代软件架构中,**权限控制**早已不
java·开发语言·rust·开源
升鲜宝供应链及收银系统源代码服务2 小时前
《IntelliJ + Claude Code + Gemini + ChatGPT 实战配置手册升鲜宝》
java·前端·数据库·chatgpt·供应链系统·生鲜配送
daidaidaiyu2 小时前
Nacos实例一则及其源码环境搭建
java·spring
小江的记录本2 小时前
【Redis】Redis全方位知识体系(附《Redis常用命令速查表(完整版)》)
java·数据库·redis·后端·python·spring·缓存
摇滚侠2 小时前
Java 项目《谷粒商城-1》架构师级Java 项目实战,对标阿里 P6-P7,全网最强,实操版本
java·开发语言
zihao_tom3 小时前
Spring Boot(快速上手)
java·spring boot·后端
hua872223 小时前
SpringSecurity之跨域
java
小王不爱笑1324 小时前
G1 GC 的核心基础:Region 模型的补充细节
java·jvm·算法
salipopl4 小时前
Spring 中的 @ExceptionHandler 注解详解与应用
java·后端·spring