原创

JAVA8系列教程-WatchService

温馨提示:
本文最后更新于 2020年04月21日,已超过 1,698 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

在此示例中,我们将学习使用Java 8 WatchServiceAPI 观察目录及其中的所有子目录和文件

如何注册Java 8 WatchService

要注册WatchService,获取目录路径并使用path.register()方法。

Path path = Paths.get(".");
WatchService watchService =  path.getFileSystem().newWatchService();
path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);

观察变化事件

要获取目录及其中文件的更改,请使用watchKey.pollEvents()方法,该方法以流的形式返回所有更改事件的集合。

WatchKey watchKey = null;
while (true) {
    watchKey = watchService.poll(10, TimeUnit.MINUTES);
    if(watchKey != null) {
        watchKey.pollEvents().stream().forEach(event -> System.out.println(event.context()));
    }
    watchKey.reset();
}

该密钥一直有效,直到:

  • 通过调用其cancel方法显式地取消它,或者
  • 隐式取消,因为该对象不再可访问,或者
  • 通过关闭手表服务。

如果要在循环中多次重复使用同一键来获取更改事件,请不要忘记调用watchKey.reset()将键重新设置为就绪状态的方法。

请注意,诸如如何检测事件,其及时性以及是否保留其顺序之类的几件事高度依赖于底层操作系统。某些更改可能导致一个操作系统中的单个条目,而类似的更改可能导致另一操作系统中的多个事件。

监视目录,子目录和文件中的更改示例

在此示例中,我们将看到一个观看目录的示例,该目录中包含所有子目录和文件。我们将维护监视键和目录的映射,Map<WatchKey, Path> keys以正确识别已修改的目录。

下面的方法将向观察者注册一个目录,然后将目录和密钥存储在地图中。

private void registerDirectory(Path dir) throws IOException
{
    WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
    keys.put(key, dir);
}

在遍历目录结构并为遇到的每个目录调用此方法时,将递归调用此方法。

private void walkAndRegisterDirectories(final Path start) throws IOException {
    // register directory and sub-directories
    Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
            registerDirectory(dir);
            return FileVisitResult.CONTINUE;
        }
    });
}

请注意,无论何时创建新目录,我们都会在watchservice中注册该目录,并将新密钥添加到地图中。

WatchEvent.Kind kind = event.kind();
if (kind == ENTRY_CREATE) {
    try {
        if (Files.isDirectory(child)) {
            walkAndRegisterDirectories(child);
        }
    } catch (IOException x) {
        // do something useful
    }
}

将以上所有内容与处理事件的逻辑放在一起,完整的示例如下所示:

package com.howtodoinjava.examples;
 
import static java.nio.file.StandardWatchEventKinds.*;
 
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;
 
public class Java8WatchServiceExample {
 
    private final WatchService watcher;
    private final Map<WatchKey, Path> keys;
 
    /**
     * Creates a WatchService and registers the given directory
     */
    Java8WatchServiceExample(Path dir) throws IOException {
        this.watcher = FileSystems.getDefault().newWatchService();
        this.keys = new HashMap<WatchKey, Path>();
 
        walkAndRegisterDirectories(dir);
    }
 
    /**
     * Register the given directory with the WatchService; This function will be called by FileVisitor
     */
    private void registerDirectory(Path dir) throws IOException
    {
        WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
        keys.put(key, dir);
    }
 
    /**
     * Register the given directory, and all its sub-directories, with the WatchService.
     */
    private void walkAndRegisterDirectories(final Path start) throws IOException {
        // register directory and sub-directories
        Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                registerDirectory(dir);
                return FileVisitResult.CONTINUE;
            }
        });
    }
 
    /**
     * Process all events for keys queued to the watcher
     */
    void processEvents() {
        for (;;) {
 
            // wait for key to be signalled
            WatchKey key;
            try {
                key = watcher.take();
            } catch (InterruptedException x) {
                return;
            }
 
            Path dir = keys.get(key);
            if (dir == null) {
                System.err.println("WatchKey not recognized!!");
                continue;
            }
 
            for (WatchEvent<?> event : key.pollEvents()) {
                @SuppressWarnings("rawtypes")
                WatchEvent.Kind kind = event.kind();
 
                // Context for directory entry event is the file name of entry
                @SuppressWarnings("unchecked")
                Path name = ((WatchEvent<Path>)event).context();
                Path child = dir.resolve(name);
 
                // print out event
                System.out.format("%s: %s\n", event.kind().name(), child);
 
                // if directory is created, and watching recursively, then register it and its sub-directories
                if (kind == ENTRY_CREATE) {
                    try {
                        if (Files.isDirectory(child)) {
                            walkAndRegisterDirectories(child);
                        }
                    } catch (IOException x) {
                        // do something useful
                    }
                }
            }
 
            // reset key and remove from set if directory no longer accessible
            boolean valid = key.reset();
            if (!valid) {
                keys.remove(key);
 
                // all directories are inaccessible
                if (keys.isEmpty()) {
                    break;
                }
            }
        }
    }
 
    public static void main(String[] args) throws IOException {
        Path dir = Paths.get("c:/temp");
        new Java8WatchServiceExample(dir).processEvents();
    }
}

运行该程序并在给定输入中更改文件和目录后,您将在控制台中注意到捕获的事件。

输出:

ENTRY_CREATE:c:\ temp \ New文件夹
ENTRY_DELETE:c:\ temp \ New文件夹
ENTRY_CREATE:c:\ temp \ data
ENTRY_CREATE:c:\ temp \ data \ New Text Document.txt
ENTRY_MODIFY:c:\ temp \ data
ENTRY_DELETE:c:\ temp \ data \ New Text Document.txt
ENTRY_CREATE:c:\ temp \ data \ tempFile.txt
ENTRY_MODIFY:c:\ temp \ data
ENTRY_MODIFY:c:\ temp \ data \ tempFile.txt
ENTRY_MODIFY:c:\ temp \ data \ tempFile.txt
ENTRY_MODIFY:c:\ temp \ data \ tempFile.txt

这就是使用Java 8 WatchService API监视文件更改并进行处理的简单示例。

学习愉快!

资源:

WatchService Java Doc
走文件树
Java 8路径
Lambda表达式

正文到此结束
本文目录