如何查看文件夹和子文件夹以进行更改

时间:2013-09-09 15:01:14

标签: java watchservice

我正在尝试查看特定文件夹以进行更改,然后如果在其中发生任何添加/编辑/删除,我需要获取该文件夹及其子文件夹中所有文件的更改类型。我正在使用WatchService,但它只观看单个路径,它不处理子文件夹。

这是我的方法:

try {
        WatchService watchService = pathToWatch.getFileSystem().newWatchService();
        pathToWatch.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
                StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);

        // loop forever to watch directory
        while (true) {
            WatchKey watchKey;
            watchKey = watchService.take(); // This call is blocking until events are present

            // Create the list of path files
            ArrayList<String> filesLog = new ArrayList<String>();
            if(pathToWatch.toFile().exists()) {
                File fList[] = pathToWatch.toFile().listFiles();
                for (int i = 0; i < fList.length; i++) { 
                    filesLog.add(fList[i].getName());
                }
            }

            // Poll for file system events on the WatchKey
            for (final WatchEvent<?> event : watchKey.pollEvents()) {
                printEvent(event);
            }

            // Save the log
            saveLog(filesLog);

            if(!watchKey.reset()) {
                System.out.println("Path deleted");
                watchKey.cancel();
                watchService.close();
                break;
            }
        }

    } catch (InterruptedException ex) {
        System.out.println("Directory Watcher Thread interrupted");
        return;
    } catch (IOException ex) {
        ex.printStackTrace();  // Loggin framework
        return;
    }

就像我之前说过的那样,我只获取所选路径中的文件的日志,并且我想要查看所有文件夹和子文件夹文件,例如:

示例1:

FileA (Created)
FileB
FileC
FolderA FileE
FolderA FolderB FileF

示例2:

FileA
FileB (Modified)
FileC
FolderA FileE
FolderA FolderB FileF

有没有更好的解决方案?

3 个答案:

答案 0 :(得分:18)

WatchService只会观看您注册的Path。它不会递归地通过这些路径。

/Root作为注册路径

/Root
    /Folder1
    /Folder2
        /Folder3

如果Folder3发生变化,则不会发现变化。

您可以使用

自行递归注册目录路径
private void registerRecursive(final Path root) throws IOException {
    // register all subfolders
    Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
            dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
            return FileVisitResult.CONTINUE;
        }
    });
}

现在WatchService将通知Path root所有子文件夹中的所有更改,即。你传递的Path参数。

答案 1 :(得分:9)

递归注册将像Sotirios指出的那样工作。这有效地注册了当前存在的每个目录/子目录。

您也可以导入和使用* com.sun.nio.file.ExtendedWatchEventModifier.FILE_TREE *,如下所示:

dir.register(watcher, standardEventsArray, ExtendedWatchEventModifier.FILE_TREE);

这将监视整个子树的更改并考虑添加的目录和子目录。

否则,您必须监视任何新目录/子目录并注册它们。删除部分目录层次结构也可能存在问题,因为每个注册的目录都有一个句柄,因此在删除结构的某些部分时需要首先删除(最低)子目录。

答案 2 :(得分:5)

我使用Java 8流和lambdas实现了类似的东西。

递归文件夹发现实现为Consumer @FunctionalInterface:

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

    Consumer<Path> register = p -> {
        if (!p.toFile().exists() || !p.toFile().isDirectory()) {
            throw new RuntimeException("folder " + p + " does not exist or is not a directory");
        }
        try {
            Files.walkFileTree(p, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                    LOG.info("registering " + dir + " in watcher service");
                    WatchKey watchKey = dir.register(watcher, new WatchEvent.Kind[]{ENTRY_CREATE}, SensitivityWatchEventModifier.HIGH);
                    keys.put(watchKey, dir);
                    return FileVisitResult.CONTINUE;
                }
            });
        } catch (IOException e) {
            throw new RuntimeException("Error registering path " + p);
        }
    };

每次创建新文件夹时都会调用上面的代码,以便在以后的阶段动态添加文件夹。 完整的解决方案和更多详细信息here