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();
}
}
Java WatchService监控指定路径下的文件新增、删除和修改(子文件夹、指定文件类型)的更多相关文章
- java监控指定路径下文件及文件夹变化
之前用jdk7的WatchService API(java.nio.file包)来做目录下的子文件监控,后改为使用commons-io包.主要有下面几点不同:1. WatchService是采用扫描式 ...
- java 压缩文件 传入文件数组,压缩文件,在指定路径下生成指定文件名的压缩文件
/** * 传入文件数组,压缩文件,在指定路径下生成指定文件名的压缩文件 * * @param files * 文件数组 * @param strZipName * 压缩文件路径及文件名 * @thr ...
- C#实现把指定文件夹下的所有文件复制到指定路径下以及修改指定文件的后缀名
1.实现把指定文件夹下的所有文件复制到指定路径下 public static void copyFiles(string path) { DirectoryInfo dir = new Directo ...
- python之实现循环查看指定路径下的所有文件---os.walk
循环查看指定路径下的所有文件.文件夹,包含隐藏文件注:“.filename” 以点开头的是隐藏文件 import os for cur_path,cur_dirs,cur_files in os.wa ...
- Python3在指定路径下递归定位文件中出现的字符串
[本文出自天外归云的博客园] 脚本功能:在指定的路径下递归搜索,找出指定字符串在文件中出现的位置(行信息). 用到的python特性: 1. PEP 318 -- Decorators for Fun ...
- 【Lua】关于遍历指定路径下所有目录及文件
关于Lua中如何遍历指定文件路径下的所有文件,需要用到Lua的lfs库. 首先创建一个temp.lua文件,用编辑器打开: 要使用lfs库,首先需要把lfs库加载进来 require("lf ...
- Python获取指定路径下所有文件的绝对路径
需求 给出制定目录(路径),获取该目录下所有文件的绝对路径: 实现 方式一: import os def get_file_path_by_name(file_dir): ''' 获取指定路径下所有文 ...
- Python小代码_15_遍历指定路径下的所有文件和文件夹,并格式化输出文件路径文件名和文件夹名,文件大小,修改时间
遍历指定路径下的所有文件和文件夹,并格式化输出文件路径文件名和文件夹名,文件大小,修改时间 import osimport datetime def print_tree(dir_path): for ...
- Windows 定时删除指定路径下N天前的日志文件
Windows 定时删除指定路径下N天前的日志文件 Windows 下bat脚本文件的内容为 1. 删除指定路径下5天前的所有文件 @echo off set SrcDir=E:\WORK\Git s ...
- 将指定路径下的所有SVG文件导出成PNG等格式的图片(缩略图或原图大小)
原文:将指定路径下的所有SVG文件导出成PNG等格式的图片(缩略图或原图大小) WPF的XAML文档(Main.xaml): <Window x:Class="SVG2Image.Ma ...
随机推荐
- 深度剖析 GROUP BY 和 HAVING 子句:优化 SQL 查询的利器
title: 深度剖析 GROUP BY 和 HAVING 子句:优化 SQL 查询的利器 date: 2025/1/14 updated: 2025/1/14 author: cmdragon ex ...
- rabbitmq部署及配置与验证-copy
1. 场景描述 朋友项目需要弄个测试环境,稍微帮忙了下,系统不复杂,但是需要自己安装mysql.Reids.Es.RabbitMq等,Mq主要用在同步用户信息与发送站内消息和短信上,RabbitMq以 ...
- 2025-01-15:执行操作可获得的最大总奖励 Ⅰ。用go语言,给定一个整数数组 rewardValues,其中包含 n 个代表奖励值的数字。 你开始时的总奖励 x 为 0,并且所有下标都是未标记状
2025-01-15:执行操作可获得的最大总奖励 Ⅰ.用go语言,给定一个整数数组 rewardValues,其中包含 n 个代表奖励值的数字. 你开始时的总奖励 x 为 0,并且所有下标都是未标记状 ...
- [ARC 058 - E]Iroha and Haiku
传送门 解题步骤 首先可以发现题目范围非常小,尤其是\(X,Y,Z\),所以考虑类似状压.数位dp.双向搜索等算法. 官方题解中给的是数位dp,那我这里就讲讲状压了 对于\(N \leq 40\),很 ...
- C#从数据库中加载照片的
从数据库中读取人员照片信息并加载到图片控件的代码 string conn = "Server=192.168.xx.xx;Database=dbName;User ID=sa;passwor ...
- VM1038:1 (in promise) MiniProgramError {"errMsg":"navigateTo:fail webview count limit exceed"} Object解决办法
在跳转的时候,出现这,有的时候回出现,有的时候不会出现: VM1038:1 (in promise) MiniProgramError {"errMsg":"naviga ...
- CTFHub-RCE漏洞wp
引言 题目共有如下类型 什么是RCE漏洞 RCE漏洞,全称是Remote Code Execution漏洞,翻译成中文就是远程代码执行漏洞.顾名思义,这是一种安全漏洞,允许攻击者在受害者的系统上远程执 ...
- linux配置maven
1.下载mavenhttps://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/ 中找到相应的版本wget https://mirrors.tun ...
- 最新demo版 | 如何0-1开发支付宝小程序之小程序页面功能介绍(三)
前两期讲了小程序开发的准备工作以及前期需要如何调试,今天我们就来介绍下开发一个支付宝小程序页面需要了解哪些信息. 一个小程序页面的整体功能的构成离不开页面展示(AXML).页面样式(ACSS)以及页面 ...
- [BZOJ3569] DZY Loves Chinese II 题解
考虑不联通的情况.图不好做,就造一棵生成树出来,由于是无向图,所以只有树边和返祖边. 发现在一条树边断开后,生成树会分成两个连通块,由覆盖这条树边的返祖边链接,只有这些返祖边也全部断开,原图才会不联通 ...