Как посмотреть папку и подпапки для изменений
Я пытаюсь посмотреть конкретную папку для изменений, а затем, если внутри нее происходит какое-либо дополнение/выпуск/удаление, мне нужно получить тип изменения всех файлов в этой папке и ее подпапках. Я использую 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
Есть ли лучшее решение?
Ответы
Ответ 1
A 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
, который вы передаете.
Ответ 2
Регистрация рекурсивно будет работать, как указал Сотириос. Это эффективно регистрирует каждый каталог/подкаталог, который в настоящее время существует.
Вы также можете импортировать и использовать * com.sun.nio.file.ExtendedWatchEventModifier.FILE_TREE * как в:
dir.register(watcher, standardEventsArray, ExtendedWatchEventModifier.FILE_TREE);
Это будет следить за всем поддеревом для изменения и учетной записью для добавления каталогов и подкаталогов.
В противном случае вам придется отслеживать любые новые каталоги/подкаталоги и регистрировать их также. Также может возникнуть проблема с удалением частей иерархии каталогов, поскольку каждый зарегистрированный каталог имеет ручку, наблюдающую за ней, поэтому сначала необходимо удалить (самые низкие) подкаталоги при удалении частей структуры.
Ответ 3
Я реализовал что-то вроде этого, используя потоки 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);
}
};
Вышеупомянутый код вызывается каждый раз, когда создается новая папка для динамического добавления папок на более поздних этапах.
Полное решение и более подробная информация здесь.