Standard concise way to copy a file in Java?

270,595

Solution 1

As toolkit mentions above, Apache Commons IO is the way to go, specifically FileUtils.copyFile(); it handles all the heavy lifting for you.

And as a postscript, note that recent versions of FileUtils (such as the 2.0.1 release) have added the use of NIO for copying files; NIO can significantly increase file-copying performance, in a large part because the NIO routines defer copying directly to the OS/filesystem rather than handle it by reading and writing bytes through the Java layer. So if you're looking for performance, it might be worth checking that you are using a recent version of FileUtils.

Solution 2

I would avoid the use of a mega api like apache commons. This is a simplistic operation and its built into the JDK in the new NIO package. It was kind of already linked to in a previous answer, but the key method in the NIO api are the new functions "transferTo" and "transferFrom".

http://java.sun.com/javase/6/docs/api/java/nio/channels/FileChannel.html#transferTo(long,%20long,%20java.nio.channels.WritableByteChannel)

One of the linked articles shows a great way on how to integrate this function into your code, using the transferFrom:

public static void copyFile(File sourceFile, File destFile) throws IOException {
    if(!destFile.exists()) {
        destFile.createNewFile();
    }

    FileChannel source = null;
    FileChannel destination = null;

    try {
        source = new FileInputStream(sourceFile).getChannel();
        destination = new FileOutputStream(destFile).getChannel();
        destination.transferFrom(source, 0, source.size());
    }
    finally {
        if(source != null) {
            source.close();
        }
        if(destination != null) {
            destination.close();
        }
    }
}

Learning NIO can be a little tricky, so you might want to just trust in this mechanic before going off and trying to learn NIO overnight. From personal experience it can be a very hard thing to learn if you don't have the experience and were introduced to IO via the java.io streams.

Solution 3

Now with Java 7, you can use the following try-with-resource syntax:

public static void copyFile( File from, File to ) throws IOException {

    if ( !to.exists() ) { to.createNewFile(); }

    try (
        FileChannel in = new FileInputStream( from ).getChannel();
        FileChannel out = new FileOutputStream( to ).getChannel() ) {

        out.transferFrom( in, 0, in.size() );
    }
}

Or, better yet, this can also be accomplished using the new Files class introduced in Java 7:

public static void copyFile( File from, File to ) throws IOException {
    Files.copy( from.toPath(), to.toPath() );
}

Pretty snazzy, eh?

Solution 4

  • These methods are performance-engineered (they integrate with operating system native I/O).
  • These methods work with files, directories and links.
  • Each of the options supplied may be left out - they are optional.

The utility class

package com.yourcompany.nio;

class Files {

    static int copyRecursive(Path source, Path target, boolean prompt, CopyOptions options...) {
        CopyVisitor copyVisitor = new CopyVisitor(source, target, options).copy();
        EnumSet<FileVisitOption> fileVisitOpts;
        if (Arrays.toList(options).contains(java.nio.file.LinkOption.NOFOLLOW_LINKS) {
            fileVisitOpts = EnumSet.noneOf(FileVisitOption.class) 
        } else {
            fileVisitOpts = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
        }
        Files.walkFileTree(source[i], fileVisitOpts, Integer.MAX_VALUE, copyVisitor);
    }

    private class CopyVisitor implements FileVisitor<Path>  {
        final Path source;
        final Path target;
        final CopyOptions[] options;

        CopyVisitor(Path source, Path target, CopyOptions options...) {
             this.source = source;  this.target = target;  this.options = options;
        };

        @Override
        FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
        // before visiting entries in a directory we copy the directory
        // (okay if directory already exists).
        Path newdir = target.resolve(source.relativize(dir));
        try {
            Files.copy(dir, newdir, options);
        } catch (FileAlreadyExistsException x) {
            // ignore
        } catch (IOException x) {
            System.err.format("Unable to create: %s: %s%n", newdir, x);
            return SKIP_SUBTREE;
        }
        return CONTINUE;
    }

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
        Path newfile= target.resolve(source.relativize(file));
        try {
            Files.copy(file, newfile, options);
        } catch (IOException x) {
            System.err.format("Unable to copy: %s: %s%n", source, x);
        }
        return CONTINUE;
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
        // fix up modification time of directory when done
        if (exc == null && Arrays.toList(options).contains(COPY_ATTRIBUTES)) {
            Path newdir = target.resolve(source.relativize(dir));
            try {
                FileTime time = Files.getLastModifiedTime(dir);
                Files.setLastModifiedTime(newdir, time);
            } catch (IOException x) {
                System.err.format("Unable to copy all attributes to: %s: %s%n", newdir, x);
            }
        }
        return CONTINUE;
    }

    @Override
    public FileVisitResult visitFileFailed(Path file, IOException exc) {
        if (exc instanceof FileSystemLoopException) {
            System.err.println("cycle detected: " + file);
        } else {
            System.err.format("Unable to copy: %s: %s%n", file, exc);
        }
        return CONTINUE;
    }
}

Copying a directory or file

long bytes = java.nio.file.Files.copy( 
                 new java.io.File("<filepath1>").toPath(), 
                 new java.io.File("<filepath2>").toPath(),
                 java.nio.file.StandardCopyOption.REPLACE_EXISTING,
                 java.nio.file.StandardCopyOption.COPY_ATTRIBUTES,
                 java.nio.file.LinkOption.NOFOLLOW_LINKS);

Moving a directory or file

long bytes = java.nio.file.Files.move( 
                 new java.io.File("<filepath1>").toPath(), 
                 new java.io.File("<filepath2>").toPath(),
                 java.nio.file.StandardCopyOption.ATOMIC_MOVE,
                 java.nio.file.StandardCopyOption.REPLACE_EXISTING);

Copying a directory or file recursively

long bytes = com.yourcompany.nio.Files.copyRecursive( 
                 new java.io.File("<filepath1>").toPath(), 
                 new java.io.File("<filepath2>").toPath(),
                 java.nio.file.StandardCopyOption.REPLACE_EXISTING,
                 java.nio.file.StandardCopyOption.COPY_ATTRIBUTES
                 java.nio.file.LinkOption.NOFOLLOW_LINKS );

Solution 5

In Java 7 it is easy...

File src = new File("original.txt");
File target = new File("copy.txt");

Files.copy(src.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
Share:
270,595
Peter
Author by

Peter

Technical senior executive specializing in creation and leadership of top-tier engineering teams that care deeply about the underlying business. Experienced developer and architect, specializing in highly scalable cloud-based SaaS applications.

Updated on May 31, 2020

Comments

  • Peter
    Peter almost 4 years

    It has always bothered me that the only way to copy a file in Java involves opening streams, declaring a buffer, reading in one file, looping through it, and writing it out to the other steam. The web is littered with similar, yet still slightly different implementations of this type of solution.

    Is there a better way that stays within the bounds of the Java language (meaning does not involve exec-ing OS specific commands)? Perhaps in some reliable open source utility package, that would at least obscure this underlying implementation and provide a one line solution?

  • Peter
    Peter over 15 years
    Very helpful - do you have any insight as to when an official release will incorporate these nio changes?
  • delfuego
    delfuego over 15 years
    I have no clue... I've scoured every source of public info I can find, and there's no mention of a possible release date. The JIRA site shows only five open issues, though, so maybe soonish?
  • Peter
    Peter over 15 years
    Thanks, useful info. I would still argue for something like Apache Commons, especially if it uses nio (properly) underneath; but I agree it is important to understand the underlying fundamentals.
  • drye
    drye over 15 years
    This is very fast also, I replaced a section of code that we were executing xCopy to copy some directories and it has increased the speed very nicely. (I did use the repository version)
  • Ravi Wallau
    Ravi Wallau about 15 years
    I agree it's important to understand the backgrounds of the nio but I still would go with the Jakarta Commons.
  • Peter
    Peter over 14 years
    Public release of Apache Commons IO still at 1.4, grrrrrrr
  • Anton K.
    Anton K. over 13 years
    Unfortunately, there are caveats. When I copied 1.5 Gb file on Windows 7, 32 bit, it failed to map the file. I had to look for another solution.
  • Jesse Glick
    Jesse Glick over 13 years
    Three possible problems with the above code: (a) if getChannel throws an exception, you might leak an open stream; (b) for large files, you might be trying to transfer more at once than the OS can handle; (c) you are ignoring the return value of transferFrom, so it might be copying just part of the file. This is why org.apache.tools.ant.util.ResourceUtils.copyResource is so complicated. Also note that while transferFrom is OK, transferTo breaks on JDK 1.4 on Linux: bugs.sun.com/bugdatabase/view_bug.do?bug_id=5056395
  • Josh
    Josh about 13 years
    I believe this updated version addresses those concerns: gist.github.com/889747
  • fmunshi
    fmunshi about 13 years
    As of Dec 2010, Apache Commons IO is at 2.0.1, which has the NIO functionality. Answer updated.
  • An̲̳̳drew
    An̲̳̳drew almost 13 years
    @Pete it doesn't look like Apache Commons uses nio yet for file copying.
  • James P.
    James P. over 12 years
    @Mark Renouf: I'm getting mixed results with this code. Not sure what's happening but copied files sometimes have zero bytes.
  • Rick Hodgin
    Rick Hodgin over 12 years
    It's amazing Java hasn't added things like this before today. Certain operations are just the absolute essentials of writing computer software. The Oracle developers of Java could learn a thing or two from operating systems, looking at what services they provide, to make it EASIER for newbies to migrate over.
  • IlDan
    IlDan over 12 years
    A warning to Android people: this is NOT included in the standard Android APIs
  • Jesse Glick
    Jesse Glick about 12 years
    There is no Path.copyTo; it is Files.copy.
  • ChrisCantrell
    ChrisCantrell over 11 years
    Ah thanks! I was not aware of the new "Files" class with all of its helper functions. It has exactly what I need. Thanks for the example.
  • Pankaj
    Pankaj over 11 years
    performance wise, java NIO FileChannel is better, read this article journaldev.com/861/4-ways-to-copy-file-in-java
  • user207421
    user207421 almost 11 years
    This code has a major problem. transferTo() must be called in a loop. It doesn't guarantee to transfer the entire amount requested.
  • user207421
    user207421 almost 11 years
    This code has a major problem. transferTo() must be called in a loop. It doesn't guarantee to transfer the entire amount requested.
  • rob
    rob almost 11 years
    If using Java 7 or newer, you can use Files.copy as suggested by @GlenBest: stackoverflow.com/a/16600787/44737
  • rob
    rob almost 11 years
    @Scott: Pete asked for a one-line solution and you're so close...it's unnecessary to wrap Files.copy in a copyFile method. I'd just put the Files.copy(Path from, Path to) at the beginning of your answer and mention that you can use File.toPath() if you have existing File objects: Files.copy(fromFile.toPath(), toFile.toPath())
  • Josh
    Josh almost 11 years
    @EJP I do not really develop in Java anymore. You might be right, so you are welcome to suggest the proper way.
  • Rup
    Rup over 10 years
    This will work, but I don't think it's better than the other answers here?
  • Rup
    Rup over 10 years
    Not all filesystems support memory mapped files though, and I think it's relatively expensive for small files.
  • Rup
    Rup over 10 years
    So the difference from the top accepted answer is that you've got the transferFrom in a while loop?
  • Uri Agassi
    Uri Agassi almost 10 years
    What does your answer add to Scott's or Glen's?
  • Kevin Sadler
    Kevin Sadler almost 10 years
    It's concise, less is more. Their answers are good and detailed, but I missed them when looking through. Unfortunately there are a lot of answers to this and a lot of them are long, obsolete and complicated and Scott and Glen's good answers got lost in that (I will give upvotes to help with that). I wonder if my answer might be improved by reducing it to three lines by knocking out the exists() and error message.
  • user207421
    user207421 almost 10 years
    @Rup It is considerably better than the other answers here, (a) because it works, and (b) because it doesn't rely on third party software.
  • user207421
    user207421 almost 10 years
    Doesn't even compile, and the createNewFile() call is redundant and wasteful.
  • user207421
    user207421 almost 10 years
    Doesn't work with any version of Java prior to 1.4, and there is nothing that guarantees a single write is sufficient.
  • Rup
    Rup almost 10 years
    @EJP OK, but it's not very smart. File copying should be an OS or filesystem operation, not an application operation: Java hopefully can spot a copy and turn it into an OS operation except by explicitly reading the file in you're stopping it doing that. If you don't think Java can do that, would you trust it to optimise 1K reads and writes into larger blocks? And if source and destination were on a remote share over a slow network then this is clearly doing unneccesary work. Yes some third party JARs are stupidly large (Guava!) but they do add lots of stuff like this done properly.
  • foz
    foz over 9 years
    On Windows Server I'm finding that files copied like this with NIO intermittently cannot be renamed or deleted, even after calling close() on everything. I believe this is due to the memory mappings that NIO creates requiring garbage collection, as covered in this post.
  • Stuart Rossiter
    Stuart Rossiter over 9 years
    The package name for Files was wrong (should be java.nio.file not java.nio). I've submitted an edit for that; hope that's OK!
  • mjs
    mjs over 9 years
    This doesn't work for directories. Damn everyone is getting this one wrong. More of an API communication issue your fault. I too got it wrong.
  • mjs
    mjs over 9 years
    First one doesn't work for directories. Damn everyone is getting this one wrong. More of an API communication issue your fault. I too got it wrong.
  • Kevin Sadler
    Kevin Sadler over 9 years
    @momo the question was how to copy a file.
  • James Wierzba
    James Wierzba about 9 years
    Worked like a charm. Best solution that does not require 3rd party libraries and works on java 1.6. Thanks.
  • user207421
    user207421 almost 9 years
    @Rup I agree that it should be an operating system function, but I can't make any other sense of your comment. The part after the first colon is lacking a verb somewhere; I would neither 'trust' not expect Java to turn 1k blocks into something larger, although I would certainly use much larger blocks myself; I would never write an application that used shared files in the first place; and I'm not aware that any third party library does anything more 'proper' (whatever you mean by that) than this code, except probably to use a larger buffer.
  • Rup
    Rup almost 9 years
    @EJP By 'proper' I meant 1) using the best available APIs, e.g. the newer IO ones where possible 2) presenting the calls to Java in a way it can easily recognize that this is a file copy - e.g. channels representing whole files to read and write, as in other answers here - and turn it into a single OS call. I think this approach is as slow as you're likely to get, particularly with a small buffer.
  • Pimp Trizkit
    Pimp Trizkit about 8 years
    First one needs 3 parameters. Files.copy using only 2 parameters is for Path to Stream. Just add the parameter StandardCopyOption.COPY_ATTRIBUTES or StandardCopyOption.REPLACE_EXISTING for Path to Path
  • aswzen
    aswzen over 7 years
    nice! this one is fast rather than standar java.io stream .. copying 10GB only in 160 seconds
  • Reinstate Monica
    Reinstate Monica over 5 years
    This code is wrong. InputStream.read can return 0 even if there is more data. InputStream.read returns -1 when there is no more data. So the loop should terminate when it returns -1, not 0.
  • Holger
    Holger about 3 years
    There’s no point in writing new java.io.File("<filepath1>").toPath() when you can use Paths.get("<filepath1>") in the first place.
  • Holger
    Holger about 3 years
    There’s no need to go the File detour when you need a Path. Files.copy(Paths.get("original.txt"), Paths.get("copy.txt"), …)