使用Java监视服务监视子文件夹

13

我正在使用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”,它只会给我放置它的路径,而不是文件名。

如何获取文件名?


2
这是一个重复的问题,地址为https://dev59.com/ZFbTa4cB1Zd3GeqP7RBW。 - Robert H
3个回答

20

Stephen C的回答中,他解释了为什么您无法获取子文件夹中创建/修改的文件名。

下面是一个简单的示例,展示了如何注册要监视的目录子目录,以便捕获您感兴趣的事件:

/**
 * 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教程:监视目录更改。您可以在那里找到非常好的解释和带有源代码的示例。特别是您会对如何监视目录(或目录树)中文件更改的示例WatchDir.java感兴趣。我提供的方法来自这个示例(为简洁起见省略了一些部分)。请仔细阅读教程以获取详细信息。

我测试了上述代码,它在OpenJDK 11上会产生NULL指针异常。但是OpenJDK已经将其关闭,并指出我们不能在构造函数中传递null。https://bugs.openjdk.java.net/browse/JDK-8133521 - Anil
@Anil 如果您查看 java.nio.file.Paths 类的源代码,您会发现 get(URI uri) 方法调用了 java.nio.file.Path.of(uri) 静态方法,在该方法内部没有检查 null,并且立即使用 uri 调用 getScheme()。因此,如果 urinull,它将导致 NPE。但这与 OP 的问题完全无关。 - informatik01

10

你之所以只看到“E:/Raja/Test”事件,而没有看到“E:/Raja/Test/Foo.txt”(例如)是因为你仅向服务注册了“E:/Raja”目录。这意味着你将在该目录及其直接成员上看到事件。 “E:/Raja/Test”是目录的一个成员,当文件添加到它时,你会收到已更改的事件。

解决方案是将“E:/Raja”的所有子目录也注册...一直到你需要到达的目录层次结构位置。


1
如果他注册了E:/Raha和E:/Raha/Test,我认为如果创建了E:/Raja/Test/Foo.txt,他将会得到两个事件的触发。 - Nick
4
他会收到通知,但这不是重点。重点在于来自E:/Raha/Test路径的观察者事件将提供一个完整的创建文件的路径名的“CREATE”事件,而另一个事件将是一个“MODIFY”事件,给出被修改的目录的路径名。(好的... 我靠记忆说的...) - Stephen C

0

我知道这样做很丑陋,但希望有人能提供更好的答案。你可以创建一个包含每个子文件夹中每个文件及其最后修改时间的列表。

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

当你收到ENTRY_MODIFY时,请比较最后修改时间。

记得更新你的列表。


3
方案2就像告诉你5岁的孩子学习滑板因为他不会骑自行车。这几乎不算解决方案,因为两者都需要学习。 - Robert H
没有解决方案2就像是告诉你5岁的孩子学习滑板,因为他想要一个有4个轮子、可以放在书包里、能够站立骑行并且可以存放在学校储物柜里的自行车一样。我已经在Java和C#中使用过文件监视器,而C#做得更好。 - Nick
4
也许C#做得更好,但是提问者可能需要使用Java,所以使用C#并不是一个解决方案。我认为这只是一个建议,而不是一个解决方案。 - Marc-Andre
我同意它应该是一个建议,我已经更改了它。 - Nick
C# 不应该作为建议,因为问题特别涉及到 Java。最多只能作为一条评论。 - Matthieu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接