1. 概要

この記事では、NIO2の興味深い機能であるFileVisitorインターフェースについて説明します。

すべてのオペレーティングシステムといくつかのサードパーティアプリケーションには、ユーザーが検索条件を定義するファイル検索機能があります。

このインターフェースは、Javaアプリケーションにそのような機能を実装するために必要なものです。 すべての.mp3ファイルを検索する必要がある場合、 .class ファイルを検索して削除するか、先月アクセスされていないすべてのファイルを検索する必要がある場合は、このインターフェイスが最適です。必要。

この機能を実装するために必要なすべてのクラスは、1つのパッケージにバンドルされています。

import java.nio.file.*;

2. FileVisitorの仕組み

FileVisitor インターフェイスを使用すると、ファイルツリーを任意の深さまでトラバースし、任意のブランチで見つかったファイルまたはディレクトリに対して任意のアクションを実行できます。

FileVisitorインターフェースの一般的な実装は次のようになります。

public class FileVisitorImpl implements FileVisitor<Path> {

    @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;
    }
}

4つのインターフェイスメソッドを使用すると、トラバーサルプロセスの重要なポイントで必要な動作を指定できます。ディレクトリにアクセスする前、ファイルにアクセスするとき、または障害が発生したときとディレクトリにアクセスした後です。

各ステージの戻り値はFileVisitResultタイプであり、トラバーサルのフローを制御します。 おそらく、ファイルツリーを調べて特定のディレクトリを探し、見つかったときにプロセスを終了するか、特定のディレクトリまたはファイルをスキップする必要があります。

FileVisitResult は、FileVisitorインターフェイスメソッドの4つの可能な戻り値の列挙型です。

  • FileVisitResult.CONTINUE –ファイルツリートラバーサルは、それを返すメソッドが終了した後も続行する必要があることを示します
  • FileVisitResult.TERMINATE –ファイルツリーのトラバーサルを停止し、それ以上のディレクトリやファイルにアクセスしなくなります
  • FileVisitResult.SKIP_SUBTREE –この結果は、 preVisitDirectory APIから返された場合にのみ意味があり、他の場所ではCONTINUEのように機能します。 これは、現在のディレクトリとそのすべてのサブディレクトリをスキップする必要があることを示しています
  • FileVisitResult.SKIP_SIBLINGS –現在のファイルまたはディレクトリの兄弟にアクセスせずにトラバーサルを続行する必要があることを示します。 preVisitDirectory フェーズで呼び出された場合、現在のディレクトリもスキップされ、postVisitDirectoryは呼び出されません。

最後に、おそらくユーザーが検索条件を定義した後、グラフィカルユーザーインターフェイスから search ボタンをクリックしたときに、トラバーサルプロセスをトリガーする方法が必要です。 これは最も単純な部分です。

Filesクラスの静的walkFileTree APIを呼び出し、トラバーサルの開始点を表す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<Path> {
    private String fileName;
    private Path startDir;

    // standard constructors
}

インターフェイスメソッドはまだ実装していません。 検索するファイルの名前と検索を開始するパスを取得するコンストラクターを作成したことに注意してください。 ファイルが見つからなかったと結論付けるための基本ケースとして、開始パスのみを使用します。

次のサブセクションでは、各インターフェイスメソッドを実装し、この特定のサンプルアプリケーションでのその役割について説明します。

3.2. preVisitDirectory API

preVisitDirectoryAPIを実装することから始めましょう。

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

前に述べたように、このAPIは、プロセスがツリー内の新しいディレクトリに遭遇するたびに呼び出されます。 その戻り値は、私たちが決定したことに応じて、次に何が起こるかを決定します。 これは、特定のディレクトリをスキップして、検索サンプルスペースからそれらを削除するポイントです。

ディレクトリを区別せず、すべてのディレクトリを検索することを選択しましょう。

3.3. visitFile API

次に、 visitFileAPIを実装します。 ここで主なアクションが発生します。 この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;
}

この例では、アクセスしているファイルの名前をチェックして、それがユーザーが検索しているファイルであるかどうかを確認しているだけです。 名前が一致する場合は、成功メッセージを出力してプロセスを終了します。

ただし、特にファイル属性セクションを読んだ後は、ここでできることがたくさんあります。 attrs パラメーターで使用可能な作成時刻、最終変更時刻、最終アクセス時刻、またはいくつかの属性を確認し、それに応じて決定することができます。

3.4. visitFileFailed API

次に、 visitFileFailedAPIを実装します。 このAPIは、特定のファイルにJVMがアクセスできない場合に呼び出されます。 おそらく、別のアプリケーションによってロックされているか、権限の問題である可能性があります。

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

失敗メッセージをログに記録し、ディレクトリツリーの残りの部分のトラバースを続行します。 グラフィカルアプリケーション内で、ダイアログボックスを使用して続行するかどうかをユーザーに尋ねるか、メッセージをどこかに記録して後で使用するためにレポートをコンパイルするかを選択できます。

3.5. postVisitDirectory API

最後に、 postVisitDirectoryAPIを実装します。 この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;
}

Files.isSameFile APIを使用して、トラバースされたばかりのディレクトリがトラバースを開始したディレクトリであるかどうかを確認します。 戻り値が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変数の値を変更することで、この例を試すことができます。 fileToSearchstartingDirまたはそのサブディレクトリのいずれかに存在する場合、成功メッセージ、または失敗メッセージが表示されます。

4. 結論

この記事では、Java 7 NIO.2ファイルシステムAPIで利用できる、あまり一般的ではない機能のいくつか、特にFileVisitorインターフェースについて説明しました。 また、ファイル検索アプリケーションを構築してその機能を実証する手順も実行しました。

この記事で使用されている例の完全なソースコードは、Githubプロジェクトで入手できます。