您的位置:首页 > 编程语言 > Java开发

Java监控文件目录的变化

2015-01-15 19:19 471 查看
在Java1.7以前的版本中,如果程序想要监控文件的变化,比较常规的做法是轮询要监控的文件目录,采用启动一条后台线程。这种做法性能较差尤其对于大批量的文件监控。第三方commons-io.jar包有org.apache.commons.io.monitor类利用观察者模式也可用做文件的监控。
1.7版本后,java.nio.file包提供了目录监控的api即 Watch Service API。我们把想要监控的目录注册进watchservice中,你把想要监控的事件告诉service,比如文件创建,删除,修改。当service检测到所关注的事件,系统的注册的线程就是检测到并做出相应处理。
WatchKeys是线程安全的,可以和 java.nio.concurrent包中的类使用也可以放入 thread
pool中使用。

基本步骤:
1,为文件系统创建WatchService

WatchService watcher = FileSystems.getDefault().newWatchService();


2,所有实现了 Watchable 接口的对象才可以被注册进watchService中。Path类实现了Watchable接口,所以我们要监控的文件路径都要获取它的Path对象。

Path path = Paths.get(fileDirectory)
Path类实现了2个register方法。我们使用 register(WatchService, WatchEvent.Kind<?>...)。有三个参数版本的register方法暂时还没有实现。(第三个参数WatchEvent.Modifier直到JDK1.8还没有给出实现)。
可以监听的事件类型有

ENTRY_CREATE
– 新文件创建
ENTRY_DELETE
– 文件删除
ENTRY_MODIFY
– 文件修改
OVERFLOW
– 表示当前的事件可能丢失或被忽略掉,通常我们不用注册这个事件。

Path dir = ...;
try {
WatchKey key = dir.register(watcher,
ENTRY_CREATE,
ENTRY_DELETE,
ENTRY_MODIFY);
} catch (IOException x) {
System.err.println(x);
}


3,事件处理的过程
首先,获取key,有三种方法获得watch key.

poll – 获取并删除就绪队列中的key。若可用,返回就绪队列中的key。不可用就返回null值。
poll(long, TimeUnit) – 与poll相同,只是等待TimeUnit的时间
take – 获取可用key.没有可用的就wait

然后,利用key.pollEvents()方法获取WatchEvents的list集合,利用kind()方法获取事件的类型,context()方法获取文件名,count()方法获取次数。

最后事件处理完后,我们需要调用key.reset()方法使key重置到ready状态。若reset()方法返回false,说明key已经失效了,循环可以退出了。这一步很重要,若没有调用reset()方法,则当前的key就不再会获取将来发生的事件。

一个watch key在它的生命周期内有以下3种状态。

Ready
表示key已经准备好接受事件。当key建立时,它就处于这个状态。
Signaled
表示有一个或多个事件在排队等待。一旦一个key被signaled,它就不处于ready状态了,知道reset()重置以后。
Invalid
表示当前key已经不可用了。. 造成Invalid的原因可能有下面几个

调用cancel方法明确的使其失效
文件系统目录变成不可进入
watch service 关闭了

一个例子:
package filewatch;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;

public class FileWatchTask implements Runnable {
private String fileDirectory;

public FileWatchTask(String fileDirectory) {
this.fileDirectory = fileDirectory;
}

public void run() {
WatchService watchService = null;
try {
//获取当前文件系统的WatchService监控对象
watchService = FileSystems.getDefault().newWatchService();
} catch (IOException e) {
e.printStackTrace();
}
try {
//获取文件目录下的Path对象注册到 watchService中。
//监听的事件类型,有创建,删除,以及修改
Paths.get(fileDirectory)
.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
} catch (IOException e) {
e.printStackTrace();
}
while (true) {
WatchKey key = null;
try {
//获取可用key.没有可用的就wait
key = watchService.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (WatchEvent<?> event : key.pollEvents()) {
//todo
System.out.println(event.context() + "文件:" + event.kind() + "次数: " + event.count());
}
//重置,这一步很重要,否则当前的key就不再会获取将来发生的事件
boolean valid = key.reset();
//失效状态,退出监听
if (!valid) {
break;
}
}
}
}


MyWatchService.java

package filewatch;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyWatchService {

public static void main(String[] args) throws Exception {
String propFileName = "D:\\sources\\"; //要监控的文件目录
//因为是线程安全的所以可以放入ThreadPool中使用
ExecutorService cachedThreadPool = Executors.newFixedThreadPool(1);
cachedThreadPool.execute(new FileWatchTask(propFileName));
}

}


结果如下

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: