Руководство по NIO2 FileVisitor

Руководство по NIO2 FileVisitor

1. обзор

В этой статье мы собираемся изучить интересную особенность NIO2 - интерфейсFileVisitor.

Все операционные системы и несколько сторонних приложений имеют функцию поиска файлов, где пользователь определяет критерии поиска.

Этот интерфейс - то, что нам нужно для реализации такой функциональности в приложении Java. Если вам нужно найти все файлы.mp3, найти и удалить файлы.class или найти все файлы, к которым не осуществлялся доступ в последний месяц, то этот интерфейс - то, что вам нужно.

Все классы, которые нам нужны для реализации этой функциональности, объединены в один пакет:

import java.nio.file.*;

2. Как работаетFileVisitor

С помощью интерфейсаFileVisitor вы можете перемещаться по дереву файлов на любую глубину и выполнять любые действия с файлами или каталогами, найденными в любой ветви.

Типичная реализация интерфейсаFileVisitor выглядит так:

public class FileVisitorImpl implements FileVisitor {

    @Override
    public FileVisitResult preVisitDirectory(
      Path dir, BasicFileAttributes attrs) {
        return null;
    }

    @Override
    public FileVisitResult visitFile(
      Path file, BasicFileAttributes attrs) {
        return null;
    }

    @Override
    public FileVisitResult visitFileFailed(
      Path file, IOException exc) {
        return null;
    }

    @Override
    public FileVisitResult postVisitDirectory(
      Path dir, IOException exc) {
        return null;
    }
}

Четыре метода интерфейса позволяют нам указать требуемое поведение в ключевых точках процесса обхода: перед обращением к каталогу, при посещении файла или при возникновении сбоя и после обращения к каталогу соответственно.

Возвращаемое значение на каждом этапе имеет типFileVisitResult и управляет потоком обхода. Возможно, вы хотите пройтись по дереву файлов в поисках определенного каталога и завершить процесс, когда он найден, или вы хотите пропустить определенные каталоги или файлы.

FileVisitResult - это перечисление четырех возможных возвращаемых значений для методов интерфейсаFileVisitor:

  • FileVisitResult.CONTINUE - указывает, что обход дерева файлов должен продолжаться после выхода возвращающего его метода

  • FileVisitResult.TERMINATE - останавливает обход дерева файлов, и никакие другие каталоги или файлы не посещаются

  • FileVisitResult.SKIP_SUBTREE - этот результат имеет смысл только при возврате из APIpreVisitDirectory, в другом месте он работает какCONTINUE. Это указывает на то, что текущий каталог и все его подкаталоги должны быть пропущены

  • FileVisitResult.SKIP_SIBLINGS - указывает, что обход должен продолжаться без посещения братьев и сестер текущего файла или каталога. Если вызывается в фазеpreVisitDirectory, то даже текущий каталог пропускается иpostVisitDirectory не вызывается

Наконец, должен быть способ запустить процесс обхода, например, когда пользователь нажимает кнопкуsearch в графическом пользовательском интерфейсе после определения критериев поиска. Это самая простая часть.

Нам просто нужно вызвать статический APIwalkFileTree классаFiles и передать ему экземпляр классаPath, который представляет начальную точку обхода, а затем экземпляр нашегоFileVisitor:

Path startingDir = Paths.get("pathToDir");
FileVisitorImpl visitor = new FileVisitorImpl();
Files.walkFileTree(startingDir, visitor);

3. Пример поиска файла

В этом разделе мы собираемся реализовать приложение для поиска файлов с использованием интерфейсаFileVisitor. Мы хотим, чтобы пользователь мог указать полное имя файла с расширением и начальный каталог для поиска.

Когда мы находим файл, мы печатаем сообщение об успехе на экране и, когда поиск по всему дереву файлов осуществляется без поиска файла, мы также печатаем соответствующее сообщение об ошибке.

3.1. Основной класс

Назовем этот классFileSearchExample.java:

public class FileSearchExample implements FileVisitor {
    private String fileName;
    private Path startDir;

    // standard constructors
}

Нам еще предстоит реализовать методы интерфейса. Обратите внимание, что мы создали конструктор, который берет имя файла для поиска и путь для начала поиска. Мы будем использовать только начальный путь в качестве базового варианта, чтобы сделать вывод, что файл не был найден.

В следующих подразделах мы реализуем каждый метод интерфейса и обсудим его роль в этом конкретном примере приложения.

3.2. APIpreVisitDirectory

Начнем с реализации APIpreVisitDirectory:

@Override
public FileVisitResult preVisitDirectory(
  Path dir, BasicFileAttributes attrs) {
    return CONTINUE;
}

Как мы уже говорили ранее, этот API вызывается каждый раз, когда процесс встречает новый каталог в дереве. Его возвращаемое значение определяет, что будет дальше, в зависимости от того, что мы решим. Это та точка, в которой мы пропускаем определенные каталоги и удаляем их из пространства образца поиска.

Давайте не будем различать каталоги и просто будем искать по всем из них.

3.3. APIvisitFile

Далее мы реализуем APIvisitFile. Вот где происходит основное действие. Этот API вызывается каждый раз, когда встречается файл. Мы используем это для проверки атрибутов файла, сравнения с нашими критериями и возврата соответствующего результата:

@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
    String fileName = file.getFileName().toString();
    if (FILE_NAME.equals(fileName)) {
        System.out.println("File found: " + file.toString());
        return TERMINATE;
    }
    return CONTINUE;
}

В нашем случае мы проверяем только имя посещаемого файла, чтобы узнать, тот ли он тот, который ищет пользователь. Если имена совпадают, мы печатаем сообщение об успехе и завершаем процесс. Тем не менее, здесь так много можно сделать, особенно после прочтения

Однако здесь можно так много сделать, особенно после прочтения разделаFile Attributes. Вы можете проверить время создания, время последнего изменения или время последнего доступа или несколько атрибутов, доступных в параметреattrs, и принять соответствующее решение.

3.4. APIvisitFileFailed

Далее мы реализуем APIvisitFileFailed. Этот API вызывается, когда конкретный файл не доступен для JVM. Возможно, оно было заблокировано другим приложением или это может быть просто проблема с разрешением:

@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
    System.out.println("Failed to access file: " + file.toString());
    return CONTINUE;
}

Мы просто регистрируем сообщение об ошибке и продолжаем обход остальной части дерева каталогов. В графическом приложении вы можете спросить пользователя, продолжать ли использовать диалоговое окно или нет, или просто зарегистрировать сообщение где-нибудь и скомпилировать отчет для дальнейшего использования.

3.5. APIpostVisitDirectory

Наконец, мы реализуем APIpostVisitDirectory. Этот API вызывается каждый раз, когда каталог полностью пересекается:

@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc){
    boolean finishedSearch = Files.isSameFile(dir, START_DIR);
    if (finishedSearch) {
        System.out.println("File:" + FILE_NAME + " not found");
        return TERMINATE;
    }
    return CONTINUE;
}

Мы используем APIFiles.isSameFile, чтобы проверить, является ли только что пройденный каталог тем каталогом, из которого мы начали обход. Если возвращаемое значение -true, это означает, что поиск завершен и файл не найден. Поэтому мы завершаем процесс сообщением об ошибке.

Однако, если возвращаемое значение -false, это означает, что мы только что закончили обход подкаталога, и все еще существует вероятность найти файл в каком-либо другом подкаталоге. Итак, мы продолжим обход.

Теперь мы можем добавить наш основной метод для выполнения приложенияFileSearchExample:

public static void main(String[] args) {
    Path startingDir = Paths.get("C:/Users/user/Desktop");
    String fileToSearch = "hibernate-guide.txt"
    FileSearchExample crawler = new FileSearchExample(
      fileToSearch, startingDir);
    Files.walkFileTree(startingDir, crawler);
}

Вы можете поэкспериментировать с этим примером, изменив значения переменныхstartingDir иfileToSearch. КогдаfileToSearch существует вstartingDir или в любом из его подкаталогов, вы получите сообщение об успешном выполнении или сообщение об ошибке.

4. Заключение

В этой статье мы исследовали некоторые из менее часто используемых функций, доступных в API файловой системы Java 7 NIO.2, в частности интерфейсFileVisitor. Нам также удалось пройти через этапы создания приложения для поиска файлов, чтобы продемонстрировать его функциональность.

Полный исходный код примеров, использованных в этой статье, доступен в папкеGithub project.