特定のフォルダーの変更を監視しようとしていますが、その中で追加/編集/削除が発生した場合は、そのフォルダーとそのサブフォルダー内のすべてのファイルの変更タイプを取得する必要があります。これには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
より良い解決策はありますか?
WatchService
は、登録したPath
sのみを監視します。これらのパスを再帰的に通過することはありません。
登録パスとして/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
引数。
Sotiriosが示しているように、再帰的に登録すると機能します。これにより、現在存在する各ディレクトリ/サブディレクトリが効果的に登録されます。
または、次のように* com.Sun.nio.file.ExtendedWatchEventModifier.FILE_TREE *をインポートして使用することもできます。
dir.register(watcher, standardEventsArray, ExtendedWatchEventModifier.FILE_TREE);
これにより、サブツリー全体の変更が監視され、追加されたディレクトリとサブディレクトリが考慮されます。
それ以外の場合は、新しいディレクトリ/サブディレクトリを監視し、それらも登録する必要があります。登録された各ディレクトリにはそれを監視するハンドルがあるため、ディレクトリ階層の一部を削除する際にも問題が発生する可能性があります。そのため、構造の一部を削除するときは、最初に(最も低い)サブディレクトリを削除する必要があります。
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);
}
};
上記のコードは、新しいフォルダーが作成されるたびに呼び出され、後の段階でフォルダーを動的に追加します。完全なソリューションと詳細 ここ 。