So you need to copy an entire folder or directory in a Java application?

And, moreover, you need to copy it recursively?

Well, that's not a problem thanks to Java NIO.

In this guide, I'll show you how to handle that with just a few lines of code.

You've Got a Visitor

But first, we've got a take a step back and look at a Java NIO interface called FileVisitor.

It's the interface you'll use when you traverse a directory tree. And since you're here to recursively copy an entire directory, it's what you're looking for.

So what does FileVisitor do? It lets you take various types of actions as you walk all over the tree. You can take action prior to visiting a directory, after visiting a directory, when you visit a file, or when you attempted to visit a file and the effort failed.

Each one of those actions, by the way, returns an enum called FileVisitResult. It gives you four self-explanatory options:

  • FileVisitResult.CONTINUE
  • FileVisitResult.SKIP_SIBLINGS
  • FileVisitResult.SKIP_SUBTREE
  • FileVisitResult.TERMINATE

It's a great idea to familiarize yourself with FileVisitor if you want to do anything recursively on a directory (like, for example, delete it).

Fortunately, the SimpleFileVisitor class already implements FileVisitor. So you can just extend that and override the methods that require customization.

And that's what you'll do next.

Keepin' It Classy

Here's your custom class for traversing the entire directory tree:

public class CustomFileVisitor extends SimpleFileVisitor<Path> {
    
    private Path source;
    private Path target;
    private CopyOption[] options;
    
    private CustomFileVisitor(Path source, Path target, CopyOption... options) {
        this.target = target;
        this.source = source;
        this.options = options;
    }
    
    @Override
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
            throws IOException {
        Files.createDirectories(target.resolve(source.relativize(dir)));
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
            throws IOException {
        Files.copy(file, target.resolve(source.relativize(file)), options);
        return FileVisitResult.CONTINUE;
    }        
}

I imaginatively called that class CustomFileVisitor but you can name it as you please.

One thing I didn't mention in the previous section is that FileVisitor is a generic. That means you need to specify the type of reference to files. I think it best to work with Path here.

The class gets instantiated with a constructor that takes three parameters: the source Path, the target Path, and 0 or more StandardCopyOptions.

The StandardCopyOption enum tells the JVM what kind of action to take when copying files. I'll revisit that one in a little bit.

The class only overrides two of the four public methods in the parent class: preVisitDirectory() and visitFIle().

In preVisitDirectory(), the code creates the new directory in the target tree. That whole relativize()/resolve() thing is a concept I've explained in a separate article, but for now all you need to know is that if the source directory is /sourceDirectory and the target directory is /targetDirectory then it will create a new subdirectory underneath /targetDirectory based on the subdirectory name under /sourceDirectory.

So if the directory specified in the dir parameter is /sourceDirectory/brian then it will create a new directory called /targetDirectory/brian.

Same concept in visitFile() except it's copying a file and not creating a directory.

Both methods return FileVisitResult.CONTINUE meaning that traversal is to continue normally once the create/copy operation is complete.

The Thing That Does the Work

Thus far, all you have is a class that looks great but it nobody is using it. It's time to create a method that uses it.

    public void copyFolder(Path source, Path target, CopyOption... options)
            throws IOException {
        
        CustomFileVisitor customFileVisitor = new CustomFileVisitor(source, target, options);
        Files.walkFileTree(source, customFileVisitor);
    }

That method is intuitively named copyFolder() because that's exactly what it does. It accepts the same three parameters as the CustomFileVisitor constructor.

In fact, it uses those parameters to instantiate CustomFileVisitor.

And then there's that static walkFileTree() method in the Files class. What's that all about?

It lives up to its name. It goes through the whole file tree, stopping at each new file and directory and obeying the commands put forth in the CustomFileVisitor object with each new item it finds.

So there's a lot more happening in Files.walkFileTree() than meets the eye. That's because all the good stuff you specified in CustomFileVisitor will execute with each new folder or file.

That means Files.walkFileTree() will, in fact, copy the source folder recursively to the destination specified in target.

Making It Happen

To get a copy going, do something like this:

    private void go() {
        final Path src = Paths.get("C:/", "home");
        final Path dest = Paths.get("C:/", "home2");
        
        try {
            copyFolder(src, dest, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException ie) {
            ie.printStackTrace();
        }
    }

That's assuming you're on Windows, of course. It will copy C:/home to C:/home2.

But pay close attention to the StandardCopyOption specification. That REPLACE_EXISTING option is as ominous as it sounds. It will overwrite any existing folders or files it finds along the way.

So be careful when you go that route.

Wrapping It Up

Now you know how to copy an entire folder recursively using Java NIO.

Over to you. Take what you've learned and tweak it to suit your own requirements.

Have fun!

Photo by Andrea Piacquadio from Pexels