Не найдя нигде толкового описания на русском, что за зверь Files.walkFileTree(), и со скрипом освоив его, как оказалось впоследствии, несложный функционал, решил поделиться в рамках закрепления материалом с примерами, чего мне так не хватало.
Метод walkFileTree() позволяет обойти дерево файлов и поддиректорий передаваемого ему в качестве параметра элемента Path…
Две сигнатуры метода
Files.walkFileTree(Path start, FileVisitor<? super Path> visitor);
Files.walkFileTree(Path start, Set<FileVisitOption> options, int maxDepth, FileVisitor<? super Path> visitor);
Где
Path start — директория, содержимое которой предстоит обойти,
FileVisitor visitor — инстанс класса, имплементирующего интерфейс FileVisitor, или наследующего от SimpleFileVisitor(). Последний, на мой взгляд, удобнее использовать, когда не требуется переопределять все методы FileVisitor(), о которых речь пойдет чуть ниже(но все зависит от ситуации),
Set<FileVisitOption> options — набор опций, определяющих поведение при обходе,
maxDepth — глубина прохода поддиректорий.
Сначала про параметры, которые рано или поздно пригодятся
maxDepth — глубина обхода. При = 0 заход в директорию не будет осуществлен, при = MAX_VALUE директория будет изучена до максимальной глубины, соответственно при maxDepth = 3, проход будет осуществляться на 3 поддиректории «вниз».
FileVisitOption — enum множество, определяющее, стоит ли программе при обходе следовать по символьным ссылкам(в этом случае указывается значение FileVisitOption.FOLLOW_LINKS).
FileVisitor — интерфейс, имеющий 4 метода:
1) FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs) throws IOException;
включает в себя набор методов, которые следует выполнить перед посещением текущей поддиректории. Например, через attrs можно получить из такие данные, как:
lastModifiedTime(),
lastAccessTime(),
creationTime(),
isRegularFile() — true, если перед нами файл,
isDirectory() — true, если перед нами директория,
isSymbolicLink() — true, если объект является ссылкой,
isOther() — true, если рассматриваемый объект не является ни файлом, ни директорией, ни ссылкой,
size() — возвращает размер объекта и
fileKey() — возвращает файловый ключ или null;
Если в процессе обхода планируется подсчет количества директорий, не следует забывать, что директория start также будет учитываться в итоговой сумме.
2) FileVisitResult visitFile(T file, BasicFileAttributes attrs) throws IOException;
набор методов, которые следует выполнить во время посещения текущего файла(можно, например, порыться в его содержимом и поискать вхождения искомой строки, или опять же выяснить, дату последнего посещения, изменения файла или сложить при обходе размер всех файлов, чтобы получить размер директории)
Пример
public class MyFileVisitor extends SimpleFileVisitor<Path> {
String partOfName;
String partOfContent;
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
boolean containsName = true;
if(partOfName!=null && !file.getFileName().toString().contains(partOfName))
containsName = false;
String content = new String(Files.readAllBytes(file));
boolean containsContent = true;
if(partOfContent!=null && !content.contains(partOfContent))
containsContent = false;
if(containsName && containsContent)
foundFiles.add(file);
return FileVisitResult.CONTINUE;
}
В данном примере при обходе дерева файлов каждый файл проверяется на предмет одновременного выполнения 2 событий — содержит ли имя файла искомое вхождение, и есть ли в теле файла вхождения требуемой строки. При выполнении обоих условий, файл добавляется в результирующий лист, после чего обход продолжается;
Или еще пример перегрузки метода visitFile
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
if(path.toString().endsWith(".rar") || path.toString().endsWith(".zip"))
archived.add(path.toString());
return FileVisitResult.CONTINUE;
}
Программа во время обхода дерева при посещении файла, в случае, если он является zip или rar архивом, добавляет его адрес(path) в виде строки к списку архивных файлов;
3) FileVisitResult visitFileFailed(T file, IOException exc) throws IOException;
Данный метод может пригодиться при ошибке доступа к файлу + он «умеет» пробрасывать Exception. Остальному его можно научить через аннотацию Override. Например, посчитать количество файлов, доступ к которым не удалось получить;
Пример
@Override
public FileVisitResult visitFileFailed(Path path, IOException exc) throws IOException {
failed.add(path.toString());
return FileVisitResult.SKIP_SUBTREE;
}
В случае неудачной попытки доступа к path, данный путь в виде строки добавляется в лист failed ему подобных, и программа продолжает обход, не посещая его поддиректории;
4) FileVisitResult postVisitDirectory(T dir, IOException exc) throws IOException;
все, что необходимо сделать после посещения директории, нужно перечислить в рамках перегрузки данного метода. Например, пройдя директорию, и уничтожив в ней все файлы, этим методом можно уничтожить и ее саму(мы же помним, что Files.delete(Path dir) удаляет директорию только в случае, если она пустая и не содержит файлов).
Пример
@Override
public FileVisitResult postVisitDirectory(Path path, IOException exc) throws IOException {
Files.delete(path);
return FileVisitResult.CONTINUE;
}
Завершают работу все вышеописанные методы, возвращая «результаты посещения» (FileVisitResult) объекта, которые принадлежат enum множеству и могут принимать следующие значения:
CONTINUE
— продолжает обход дерева;TERMINATE
— заканчивает обход дерева;SKIP_SUBTREE
— продолжает обход, без захода в данную директорию;SKIP_SIBLINGS
— исключает из обхода «родственников» данного файла или директории;
Автор: Барак Адама