A Guide To NIO2 FileVisitor – NIO2 FileVisitor指南

最后修改: 2016年 12月 3日

1. Overview


In this article, we are going to explore an interesting feature of NIO2 – the FileVisitor interface.


All operating systems and several third party applications have a file search function where a user defines search criteria.


This interface is what we need to implement such a functionality in a Java application. Should you need to search for all .mp3 files, find and delete .class files or find all files that haven’t been accessed in the last month, then this interface is what you need.


All the classes we need to implement this functionality are bundled in one package:


import java.nio.file.*;

2. How FileVisitor Works


With the FileVisitor interface, you can traverse the file tree to any depth and perform any action on the files or directories found on any branch.


A typical implementation of the FileVisitor interface looks like this:


public class FileVisitorImpl implements FileVisitor<Path> {

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

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

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

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

The four interface methods allow us to specify the required behavior at key points in the traversal process: before a directory is accessed, when a file is visited, or when a failure occurs and after a directory is accessed respectively.


The return value at each stage is of type FileVisitResult and controls the flow of the traversal. Perhaps you want to walk the file tree looking for a particular directory and terminate the process when it is found or you want to skip specific directories or files.


FileVisitResult is an enum of four possible return values for the FileVisitor interface methods:


  • FileVisitResult.CONTINUE – indicates that the file tree traversal should continue after the method returning it exits
  • FileVisitResult.TERMINATE – stops the file tree traversal and no further directories or files are visited
  • FileVisitResult.SKIP_SUBTREE – this result is only meaningful when returned from the preVisitDirectory API, elsewhere, it works like CONTINUE. It indicates that the current directory and all its subdirectories should be skipped
  • FileVisitResult.SKIP_SIBLINGS – indicates that traversal should continue without visiting the siblings of the current file or directory. If called in the preVisitDirectory phase, then even the current directory is skipped and the postVisitDirectory is not invoked

Finally, there has to be a way to trigger the traversal process, perhaps when the user clicks the search button from a graphical user interface after defining search criteria. This is the simplest part.


We just have to call the static walkFileTree API of the Files class and pass to it an instance of Path class which represents the starting point of the traversal and then an instance of our FileVisitor:

我们只需调用Files类的静态walkFileTree API,并传递给它一个代表遍历起点的Path类的实例,然后传递给我们的FileVisitor的实例。

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

3. File Search Example


In this section, we are going to implement a file search application using the FileVisitor interface. We want to make it possible for the user to specify the complete file name with extension and a starting directory in which to look.


When we find the file, we print a success message to the screen and when the entire file tree is searched without the file being found, we also print an appropriate failure message.


3.1. The Main Class


We will call this class FileSearchExample.java:


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

    // standard constructors

We are yet to implement the interface methods. Notice that we have created a constructor which takes the name of the file to search for and the path to start searching from. We will only use the starting path as a base case to conclude that the file has not been found.


In the following subsections, we will implement each interface method and discuss it’s role in this particular example application.


3.2. The preVisitDirectory API

3.2.preVisitDirectory API

Let’s start by implementing the preVisitDirectory API:

让我们从实现preVisitDirectory API开始。

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

As we say earlier, this API is called each time the process encounters a new directory in the tree. Its return value determines what will happen next depending on what we decide. This is the point at which we would skip specific directories and eliminate them from the search sample space.


Let’s choose not to discriminate any directories and just search in all of them.


3.3. The visitFile API

3.3.visitFile API

Next, we will implement the visitFile API. This is where the main action happens. This API is called everytime a file is encountered. We take advantage of this to check the file attributes and compare with our criteria and return an appropriate result:

接下来,我们将实现visitFile API。这里是主要行动发生的地方。每当遇到一个文件,这个API就会被调用。我们利用这一点来检查文件属性,与我们的标准进行比较,并返回一个适当的结果。

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;

In our case, we are only checking the name of the file being visited to know if it’s the one the user is searching for. If the names match, we print a success message and terminate the process.


However, there is so much one can do here, especially after reading the File Attributes section. You could check created time, last modified time or last accessed times or several attributes available in the attrs parameter and decide accordingly.


3.4. The visitFileFailed API

3.4.visitFileFailed API

Next, we will implement the visitFileFailed API. This API is called when a specific file is not accessible to the JVM. Perhaps it has been locked by another application or it could just be a permission issue:

接下来,我们将实现visitFileFailed API。当JVM无法访问某个特定文件时,就会调用这个API。也许它已经被另一个应用程序锁定,或者可能只是一个权限问题。

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

We simply log a failure message and continue with traversal of the rest of the directory tree. Within a graphical application, you could choose to ask the user whether to continue or not using a dialog box or just log the message somewhere and compile a report for later use.


3.5. The postVisitDirectory API

3.5.postVisitDirectory API

Finally, we will implement the postVisitDirectory API. This API is called each time a directory has been fully traversed:

最后,我们将实现postVisitDirectory API。每当一个目录被完全遍历时,就会调用这个API。

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;

We use the Files.isSameFile API to check if the directory that has just been traversed is the directory where we started traversal from. If the return value is true, that means the search is complete and the file has not been found. So we terminate the process with a failure message.

我们使用Files.isSameFile API来检查刚刚被遍历的目录是否就是我们开始遍历的目录。如果返回值是true,这意味着搜索已经完成,文件没有被找到。所以我们用一个失败的消息来终止这个进程。

However, if the return value is false, that means we just finished traversing a subdirectory and there is still a probability of finding the file in some other subdirectory. So we continue with traversal.


We can now add our main method to execute the FileSearchExample application:


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

You can play around with this example by changing the values of startingDir and fileToSearch variables. When fileToSearch exists in startingDir or any of its subdirectories, then you will get a success message, else, a failure message.

你可以通过改变 startingDirfileToSearch 变量的值来玩这个例子。当 fileToSearch 存在于 startingDir 或其任何子目录中时,你将得到一个成功信息,否则,将得到一个失败信息。

4. Conclusion


In this article, we have explored some of the less commonly used features available in the Java 7 NIO.2 filesystem APIs, particularly the FileVisitor interface. We have also managed to go through the steps of building a file search application to demonstrate its functionality.

在这篇文章中,我们探讨了Java 7 NIO.2文件系统API中的一些不太常用的功能,特别是FileVisitor接口。我们还设法通过构建一个文件搜索应用程序的步骤来演示其功能。

The full source code for the examples used in this article is available in the Github project.