web-dev-qa-db-ja.com

フォルダーとサブフォルダーの変更を監視する方法

特定のフォルダーの変更を監視しようとしていますが、その中で追加/編集/削除が発生した場合は、そのフォルダーとそのサブフォルダー内のすべてのファイルの変更タイプを取得する必要があります。これには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

より良い解決策はありますか?

13
Yuri Heupa

WatchServiceは、登録したPathsのみを監視します。これらのパスを再帰的に通過することはありません。

登録パスとして/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;
        }
    });
}

これで、WatchServicePath rootのすべてのサブフォルダー内のすべての変更を通知します。渡すPath引数。

21

Sotiriosが示しているように、再帰的に登録すると機能します。これにより、現在存在する各ディレクトリ/サブディレクトリが効果的に登録されます。

または、次のように* com.Sun.nio.file.ExtendedWatchEventModifier.FILE_TREE *をインポートして使用することもできます。

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

これにより、サブツリー全体の変更が監視され、追加されたディレクトリとサブディレクトリが考慮されます。

それ以外の場合は、新しいディレクトリ/サブディレクトリを監視し、それらも登録する必要があります。登録された各ディレクトリにはそれを監視するハンドルがあるため、ディレクトリ階層の一部を削除する際にも問題が発生する可能性があります。そのため、構造の一部を削除するときは、最初に(最も低い)サブディレクトリを削除する必要があります。

11
mjash

Java 8ストリームとラムダを使用してこのようなものを実装しました。

再帰的なフォルダー検出は、 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);
        }
    };

上記のコードは、新しいフォルダーが作成されるたびに呼び出され、後の段階でフォルダーを動的に追加します。完全なソリューションと詳細 ここ

5