[NIO.2] 第四十篇 监控目录树

现在,我们编写一个应用来监控 C:\rafaelnadal 目录。此外,如果在这个目录中发生了 CREATE 事件创建了一个新目录,那么这个目录将会被立即注册,和 C:\rafaelnadal 中一开始就存在的目录一样。

首先,创建监控服务:

private WatchService watchService = FileSystems.getDefault().newWatchService();

然后,需要注册目录树的创建、删除和修改事件。稍微麻烦的一点是我们需要注册 C:\rafaelnadal 的所有子目录,而不仅仅只是一个目录。因此需要递归遍历所有子目录,并将其独立注册到监控服务中。要完成这个任务可以使用 SimpleFileVisitor 类,并覆盖 preVisitDirectory 方法,如果你需要处理一些未知的遍历异常,还可以覆盖 visitFileFailed() 方法。接下来,我们按照前面这些描述编写一个方法,名为 registerTree():

private void registerTree(Path start) throws IOException { 

Files.walkFileTree(start, new SimpleFileVisitor<Path>() {

@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
System.out.println("Registering:" + dir);
registerPath(dir);
return FileVisitResult.CONTINUE;
}
});
}


如你所见,上面的方法将注册的代码单独放到了 registerPath() 方法中:

private void registerPath(Path path) throws IOException { 

//register the received path
WatchKey key = path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
}


现在,所有 C:\rafaelnadal 树下的子目录都注册了新增,修改和删除事件。

接下来,我们需要使用无限循环捕获这些事件。当事件发生后,我们更关注是否是新增事件,如果是新增子目录,那么新增的子目录也需要立即调用 registerTree() 注册到监控服务中。现在有个问题是我们不知道 WatchKey 对应的 Path,因此没办法调用 registerTree() 进行注册。解决办法是将 WatchKey 和对应的 Path 存放到 HashMap 中,在 registerPath() 方法中更新 HashMap 的内容。这样,如果事件发生后,我们就可以直接从 HashMap 中获取 Path 对象:

private final Map<WatchKey, Path> directories = new HashMap<>(); 

private void registerPath(Path path) throws IOException {
//register the received path
WatchKey key = path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);

//store the key and path
directories.put(key, path);
}


现在,在无限循环中可以从 HashMap 获取 Path:


while (true) {

if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
final Path directory_path = directories.get(key);
final Path child = directory_path.resolve(filename);

if (Files.isDirectory(child, LinkOption.NOFOLLOW_LINKS)) {
registerTree(child);
}
}

}


HashMap 还可以作为结束无线循环的条件,当 WatchKey 无效时,从 HashMap 中移除,并在 HashMap 为空时跳出循环:


while (true) {

//reset the key
boolean valid = key.reset();

//remove the key if it is not valid
if (!valid) {
directories.remove(key);

if (directories.isEmpty()) {
break;
}
}
}


综上所述,编写一个完整的应用:

import java.io.IOException; 
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;

class WatchRecursiveRafaelNadal {

private WatchService watchService;
private final Map<WatchKey, Path> directories = new HashMap<>();

private void registerPath(Path path) throws IOException {
//register the received path
WatchKey key = path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);

//store the key and path
directories.put(key, path);
}
private void registerTree(Path start) throws IOException {

Files.walkFileTree(start, new SimpleFileVisitor<Path>() {

@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
System.out.println("Registering:" + dir);
registerPath(dir);
return FileVisitResult.CONTINUE;
}
});

}

public void watchRNDir(Path start) throws IOException, InterruptedException {

watchService = FileSystems.getDefault().newWatchService();

registerTree(start);

//start an infinite loop
while (true) {

//retrieve and remove the next watch key
final WatchKey key = watchService.take();

//get list of events for the watch key
for (WatchEvent<?> watchEvent : key.pollEvents()) {

//get the kind of event (create, modify, delete)
final Kind<?> kind = watchEvent.kind();

//get the filename for the event
final WatchEvent<Path> watchEventPath = (WatchEvent<Path>) watchEvent;
final Path filename = watchEventPath.context();

//handle OVERFLOW event
if (kind == StandardWatchEventKinds.OVERFLOW) {
continue;
}

//handle CREATE event
if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
final Path directory_path = directories.get(key);
final Path child = directory_path.resolve(filename);

if (Files.isDirectory(child, LinkOption.NOFOLLOW_LINKS)) {
registerTree(child);
}
}
//print it out
System.out.println(kind + " -> " + filename);
}

//reset the key
boolean valid = key.reset();

//remove the key if it is not valid
if (!valid) {
directories.remove(key);

//there are no more keys registered
if (directories.isEmpty()) {
break;
}
}
}
watchService.close();
}
}

public class Main {

public static void main(String[] args) {

final Path path = Paths.get("C:/rafaelnadal");
WatchRecursiveRafaelNadal watch = new WatchRecursiveRafaelNadal();

try {
watch.watchRNDir(path);
} catch (IOException | InterruptedException ex) {
System.err.println(ex);
}

}
}


测试的时候,可以在子目录中创建目录或文件,删除移动目录或文件,观察输出结果。

文章来源:[url]http://www.aptusource.org/2014/04/nio-2-watching-a-directory-tree/[/url]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值