How to Copy a File in Java

Copying Files in Java

Copying a file or directory used to be a typical development task. With the introduction of Docker containers and a desire for maximum immutability, we see it less and less often.

Still, it's a fundamental concept, and it might be useful to know what options does the developer have when they need to copy a file.

I/O Streams

Before Java 1.5, the standard way to copy a file was by utilizing I/O Streams:

private void copyFile(File src, File dest) throws IOException {  
    InputStream inputStream = null;
    OutputStream outputStream = null;
    try {
        inputStream = new FileInputStream(source);
        outputStream = new FileOutputStream(dest);

        // the size of the buffer doesn't have to be exactly 1024 bytes, try playing around with this number and see what effect it will have on the performance
        byte[] buffer = new byte[1024];
        int length = 0;
        while ((length = is.read(buffer)) > 0) {
            os.write(buffer, 0, length);
        }
    } finally {
        is.close();
        os.close();
    }
}

Channels and java.nio

With the major refactoring of the Java platform and Java 1.4 release in 2000, the package java.nio was introduced.

The File copying process started to look a bit more elegant and lean:

private void copyFileUsingChannel(File src, File dest) throws IOException {  
    FileChannel sourceChannel = null;
    FileChannel destinationChannel = null;
    try {
        sourceChannel = new FileInputStream(src).getChannel();
        destinationChannel = new FileOutputStream(dest).getChannel();
        destinationChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
       } finally {
           sourceChannel.close();
           destinationChannel.close();
       }
}

The most notable difference is that we got rid of the while loop and we don't control the buffer size directly anymore.

Files Helper Class

Along with Java 7, released in 2011, a helper class was added that lets us copy files in a single line of code:

private static void copyFile(File src, File dest) throws IOException {  
    Files.copy(src.toPath(), dest.toPath());
}

Taking a look at the API documentation, we see that it's possible to provide an additional option to the copy() method.

The following options from the StandardCopyOption and LinkOption enums are supported:

  • REPLACE_EXISTING – Performs the copy even when the target file already exists. If the target is a symbolic link, the link itself is copied (and not the target of the link). If the target is a non-empty directory, the copy fails with the FileAlreadyExistsException exception.

  • COPY_ATTRIBUTES – Copies the file attributes associated with the file to the target file. The exact file attributes supported are file system and platform dependent, but last-modified-time is supported across platforms and is copied to the target file.

  • NOFOLLOW_LINKS – Indicates that symbolic links should not be followed. If the file to be copied is a symbolic link, the link is copied (and not the target of the link).

Apache Commons IO and Google Guava

These two very handy helper libraries provide a very similar API for copying files. The only downside of using any of those is having to append another heavy .jar file into the project.

But if it's already there, then why not?

private void copyFileApacheCommons(File src, File dest) throws IOException {  
    FileUtils.copyFile(src, dest);
}
private void copyFileGoogleGuava(File src, File dest) throws IOException {  
    Files.copy(src, dest);
}

Bonus

Wait, but what I have a String representing the filename and not the actual File object?

No worries, we have it covered!

private File filenameToFile(String filename) throws IOException {  
    return new File(filename);
}

Which one should you use?

I/O Streams and Channels are a viable option to play around and understand how does the process look from the inside.

Another one would be if you're working on a legacy project with an outdated version of Java, and updating it isn't an option. In all other cases, you should check whether Apache Commons or Google Guava is already in the list of dependencies and choose between one of those.

If they're not, then you shouldn't add them for the sake of this single utility method and use Java 7 Files helper class.

Author image
Ukraine, Kiev Twitter Github