Sometimes you may need to recursively visit all files and subdirectories of given directory and perform some actions on it. The typical use case is searching files of a given type, copying or removing a directory including all its contents. The task is not very difficult but doing it manually is a bit cumbersome. The good thing is that Java 7 introduced a very convenient way of doing it using FileVisitor.

General workflow

When you want to recursively visit specific directory, you call one of the following methods from Files class:

Path walkFileTree(Path start, FileVisitor<? super Path> visitor); Path walkFileTree(Path start, Set<FileVisitOption> options, int maxDepth, FileVisitor<? super Path> visitor);

The first method begins recursive (depth-first) traversal process starting from the path specified as the first argument and invokes appropriate methods on visitor when some events occur. Actually, there are four possible events and they relate to the following methods of FileVisitor interface:

preVisitDirectory – Called when entering a directory (before any entry of a directory is visited)

postVisitDirectory – Called when leaving a directory (after all entries of a directory were visited including its subdirectories)

visitFile – Called when visiting the file

visitFileFailed – Called when some error occurred when visiting given file or directory

The second walkFileTree method gives more control by allowing to specify special options and maximum depth of the recursive traversal. At the moment there is only one available option FOLLOW_LINKS which indicates whether the traversal follows symbolic links.

It’s worth noting that the first method does not follow symbolic links and does not limit the depth of the traversal.

Controlling visiting process

Each method of FileVisitor returns FileVisitResult enumeration value which indicates whether and how to continue the traversal. It can have four possible values:

CONTINUE – Specifies that the traversal should continue normally

SKIP_SUBTREE – Specifies that given directory and its contents should not be traversed (they should be skipped). This value makes sense only for preVisitDirectory

SKIP_SIBLINGS – Specifies that siblings of the current file or directory should be skipped

TERMINATE – Specifies that the traversal should be aborted immediately without invoking any other callbacks

In a typical case CONTINUE should be used when traversing.

Handling errors

When an error occurs during the traversal (e.g. permission denied error for a file or directory), no exception is thrown but instead method visitFileFailed from FileVisitor interface is called with the reference to that exception. Within this method you may decide whether to ignore the error, handle it somehow or to explicitly throw the exception which will in turn terminate the traversal.

Adapter SimpleFileVisitor

If you don’t want to override all methods of FileVisitor, you may choose to extend its SimpleFileVisitor adapter class. Methods in this class continue traversal normally unless there is an error, in which case they throw the IOException.

Example

Below is a simple implementation of FileVisitor which prints the visited files and directories on screen and also calculates few simple statistics:

package com.example.filevisitor; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.FileVisitor; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; class PrintingFileVisitor implements FileVisitor<Path> { private static final String TAB = " "; private static final int TAB_SIZE = TAB.length(); private StringBuffer prefix; private int fileCount; private int directoryCount; public PrintingFileVisitor() { prefix = new StringBuffer(); fileCount = 0; directoryCount = 0; } @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { System.out.printf("%s%s:%n", prefix, dir.getFileName()); prefix.append(TAB); directoryCount++; return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { prefix.setLength(prefix.length() - TAB_SIZE); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { System.out.printf("%s%s%n", prefix, file.getFileName()); fileCount++; return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { System.out.printf("%s%s (failed)%n", prefix, file.getFileName()); return FileVisitResult.CONTINUE; } public void printSummary() { System.out.printf("Files: %d Directories: %d%n", fileCount, directoryCount); } }

The most important thing to notice here is that the visitor continue the traversal normally in case of errors instead of terminating it.

The actual traversal is started using the method:

private static void visitRecursively(Path path) throws IOException { PrintingFileVisitor visitor = new PrintingFileVisitor(); Files.walkFileTree(path, visitor); visitor.printSummary(); }

The complete code for this example can be found at GitHub.