NIO2 FileVisitorへのガイド

NIO2 FileVisitorガイド

1. 概要

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

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

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

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

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

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クラスのインスタンスを渡し、次にPathのインスタンスを渡す必要があります。 t3)s:

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

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

ただし、特にFile Attributesセクションを読んだ後は、ここでできることがたくさんあります。 作成時刻、最終変更時刻、最終アクセス時刻、または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アプリケーションを実行するためのmainメソッドを追加できます。

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 projectで入手できます。