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 ...
随机推荐
- PHP 安装启用imagick(解决 word press可选的模组imagick未被安装或已被禁用)
本教程仅适用Windows Servier IIS网站服务器. 我的博客使用IIS搭建,相比Linux,相关的教程格外少.因此让以后的小伙伴也能马上解决问题,分享此方法. 首先需要下载php对应版本的 ...
- 彻底讲透Spring Bean生命周期,源码深度剖析!
前言本篇文章主要是要介绍如何在Spring IoC 容器中 如何管理Spring Bean生命周期. 在应用开发中,常常需要执行一些特定的初始化工作,这些工作都是相对比较固定的,比如建立数据库连接,打 ...
- 分布式文件系统KFS基础知识介绍
Kosmos distributed file system,简称KFS,是一个类GFS的分布式文件系统,被设计用于分布式的结构化存储.下面将对KFS的体系结构进行简单介绍,最后给出一个使用KFS C ...
- Elasticsearch(6) --- Query查询和Filter查询
这篇博客主要分为 :Query查询和Filter查询.有关复合查询.聚合查询也会单独写篇博客. 一.概念 1.概念 一个查询语句究竟具有什么样的行为和得到什么结果,主要取决于它到底是处Query还是F ...
- MySQL的7种JOIN
原文链接:https://blog.liuzijian.com/post/61e35b3c-fae7-4e0b-aaa2-1d1f2896d9b1.html -- 创建数据库 CREATE DATAB ...
- FastReport如实现打印固定行数,不足补打空白行(转)
看了网上的一些资料,发现了方法,但是描述都不是很详细,也至于每次都无法实现,只能在数据集中做补空行处理.今天终于弄通了,贴出方法,以备后续之用. 1. <1>在报表上加一个Child(在控 ...
- HTML标签-form表单
HTML标签-form表单 在Web开发中,HTML表单(form)是不可或缺的一部分,它承担着用户与Web服务器之间交互的重任.今天,我们就来详细探讨一下HTML中的form表单标签. 一.form ...
- Luogu P3041 USACO12JAN Video Game G 题解 [ 紫 ] [ AC 自动机 ] [ 动态规划 ]
Video Games G:弱智紫题,30min 切了,dp 思路非常板. 多模式串一看肯定就是要建出 AC 自动机,然后在 fail 树里下传标记,预处理每个节点到达后的得分. 然后设计 \(dp_ ...
- Linux驱动---字符设备
目录 一.基础简介 1.1.Linux设备驱动分类 1.2.字符设备驱动概念 二.驱动基本组成 2.1.驱动模块的加载和卸载 2.2.添加LICENNSE以及其他信息 三.字符设备驱动开发步骤 3.1 ...
- 【译】HTTP 文件更新了请求变量
许多用户都要求在 Visual Studio 的 HTTP 文件中添加对请求变量的支持.使用请求变量,您可以发送 HTTP 请求,然后在从 HTTP 文件发送的任何后续请求中使用响应或请求中的数据.我 ...