【问题标题】:Monitor subfolders with a Java watch service使用 Java 监视服务监视子文件夹
【发布时间】:2013-05-17 14:22:50
【问题描述】:

我正在使用watchKey 来监听特定文件夹中的文件更改。

Path _directotyToWatch = Paths.get("E:/Raja");
WatchService watcherSvc = FileSystems.getDefault().newWatchService();
WatchKey watchKey = _directotyToWatch.register(watcherSvc, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);

while (true) {
    watchKey=watcherSvc.take();
    for (WatchEvent<?> event: watchKey.pollEvents()) {
        WatchEvent<Path> watchEvent = castEvent(event);
        System.out.println(event.kind().name().toString() + " " + _directotyToWatch.resolve(watchEvent.context()));
        watchKey.reset();
    }
}


它对我来说很好。如果我修改 raja 文件夹中的文件,它会给我带有路径的文件名。但是,当我将一些文件放在“E:/Raja/Test”之类的子文件夹中时,它只会给我放置它的路径,而不是文件名。

如何获取文件名?

【问题讨论】:

标签: java watchservice


【解决方案1】:

Stephen C 在他的answer 中给出了为什么您没有在子文件夹中创建/修改文件名的原因。

这是一个如何注册目录子目录的简单示例,以查看您感兴趣的事件:

/**
 * Register the given directory and all its sub-directories with the WatchService.
 */
private void registerAll(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 {
            dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
            return FileVisitResult.CONTINUE;
        }

    });

}

查看官方 Java 教程:Watching a Directory for Changes。在那里你可以找到非常好的解释和源代码示例。

您将对这个如何查看目录(或目录树)的文件更改的示例感兴趣:WatchDir.java

我上面提供的方法取自这个例子(为了简洁省略了一些部分)。
阅读教程了解详情。

【讨论】:

  • 我测试了上面的代码,它在 OpenJDK 11 上给出了 NULL 指针异常。但是 OpenJDK 已经关闭了这个问题,因为我们不能在构造函数中传递 null。 bugs.openjdk.java.net/browse/JDK-8133521
  • @Anil 如果查看java.nio.file.Paths 类的源代码,您会看到get(URI uri) 方法调用java.nio.file.Path.of(uri) 静态方法,其中没有检查null并且uri 立即用于调用getScheme()。所以难怪如果urinull,就会导致NPE。但这与OP的问题完全无关。
【解决方案2】:

您只看到“E:/Raja/Test”而不是“E:/Raja/Test/Foo.txt”的事件(例如)的原因是您只注册了“E:/ Raja" 服务目录。这意味着您将在目录及其直接成员上看到事件。 “E:/Raja/Test”是该目录的一个成员,当文件被添加到它时,你会收到事件说它已经被改变了。

解决方案是同时注册“E:/Raja”的所有子目录...根据需要在目录层次结构中向下移动。

【讨论】:

  • 如果他注册 E:/Raha 和 E:/Raha/Test 我认为如果创建 E:/Raja/Test/Foo.txt 他会触发两个事件。
  • 是的,他会的。但这不是重点。关键是来自 E:/Raha/Test 上的观察者的事件将给他一个 CREATE 事件,该事件是创建文件的完整路径名,但另一个将是一个 MODIFY 事件,给出已修改目录的路径名。 (好吧……我从记忆中走了……)
【解决方案3】:

我知道这很难看,希望有人有更好的答案,但您可以创建每个子文件夹中每个文件的列表,并在那里最后修改时间。

当您收到 ENTRY_CREATE 或 ENTRY_DELETE 时,将文件夹与您的列表进行比较,以确定哪个文件被更改

当您收到 ENTRY_MODIFY 时,比较上次修改时间。

记得更新你的列表。

【讨论】:

  • 解决方案 2 就像告诉你 5 岁的孩子学习如何滑板,因为他不会骑自行车。几乎不是一个解决方案,因为两者都需要学习。
  • 没有解决方案 2 就像告诉你 5 岁的孩子学习如何骑滑板,因为他想要一辆有 4 个*的自行车,他可以装在书包里,以站立姿势骑行并且存放在他的学校储物柜里。我已经在 java 和 C# 中完成了 filewatchers。 C# 做得更好
  • 也许 C# 做得更好,但 OP 可能需要使用 Java,因此使用 C# 不是解决方案。我认为这是一个建议而不是解决方案。
  • 我同意这应该是我改变它的建议。
  • C# 不应该是一个建议,因为这个问题专门针对 Java。一个评论,充其量......