430

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
  • 29,498
  • 21
  • 89
  • 122
  • 5
    There could be something in Apache Commons [FileUtils](http://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/FileUtils.html), Specifically, the ***copyFile*** methods. – toolkit Sep 20 '08 at 02:04
  • 24
    If using Java 7, use Files.copy instead, as recommended by @GlenBest: http://stackoverflow.com/a/16600787/44737 – rob Jun 17 '13 at 21:54

16 Answers16

280

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.

James P.
  • 19,313
  • 27
  • 97
  • 155
Josh
  • 17,834
  • 7
  • 50
  • 68
  • 2
    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. – Peter Sep 22 '08 at 17:19
  • I agree it's important to understand the backgrounds of the nio but I still would go with the Jakarta Commons. – Ravi Wallau Mar 04 '09 at 21:35
  • 1
    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. – Anton K. Jan 12 '11 at 08:48
  • 16
    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: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5056395 – Jesse Glick Jan 28 '11 at 00:41
  • 7
    I believe this updated version addresses those concerns: https://gist.github.com/889747 – Mark Renouf Mar 27 '11 at 23:15
  • @Pete it doesn't look like Apache Commons uses nio yet for file copying. – An̲̳̳drew Jun 29 '11 at 13:48
  • @Mark Renouf: I'm getting mixed results with this code. Not sure what's happening but copied files sometimes have zero bytes. – James P. Aug 15 '11 at 13:23
  • 11
    This code has a *major* problem. transferTo() must be called in a loop. It doesn't guarantee to transfer the entire amount requested. – user207421 Jun 01 '13 at 01:30
  • @EJP I do not really develop in Java anymore. You might be right, so you are welcome to suggest the proper way. – Josh Jun 19 '13 at 23:13
  • 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](http://stackoverflow.com/q/21337078/1671320). – foz Aug 26 '14 at 11:39
277

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.

Steve Blackwell
  • 5,904
  • 32
  • 49
delfuego
  • 14,085
  • 4
  • 39
  • 39
  • 1
    Very helpful - do you have any insight as to when an official release will incorporate these nio changes? – Peter Sep 20 '08 at 03:01
  • 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? – delfuego Sep 21 '08 at 02:04
  • 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) – drye Oct 01 '08 at 12:47
  • 2
    Public release of Apache Commons IO still at 1.4, grrrrrrr – Peter Sep 02 '09 at 23:05
  • 14
    As of Dec 2010, Apache Commons IO is at 2.0.1, which has the NIO functionality. Answer updated. – Simon Nickerson Apr 08 '11 at 10:08
  • 4
    A warning to Android people: this is NOT included in the standard Android APIs – IlDan Feb 06 '12 at 10:46
  • 18
    If using Java 7 or newer, you can use Files.copy as suggested by @GlenBest: http://www.stackoverflow.com/a/16600787/44737 – rob Jun 17 '13 at 21:57
183

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?

Scott
  • 2,233
  • 2
  • 15
  • 8
  • 15
    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. – Rick Hodgin Oct 18 '11 at 22:30
  • 2
    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. – ChrisCantrell Nov 27 '12 at 00:26
  • 1
    performance wise, java NIO FileChannel is better, read this article http://www.journaldev.com/861/4-ways-to-copy-file-in-java – Pankaj Dec 04 '12 at 00:36
  • 5
    This code has a *major* problem. transferTo() must be called in a loop. It doesn't guarantee to transfer the entire amount requested. – user207421 Jun 01 '13 at 01:30
  • @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()) – rob Jun 18 '13 at 17:13
94
  • 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 );
randers
  • 5,031
  • 5
  • 37
  • 64
Glen Best
  • 22,769
  • 3
  • 58
  • 74
  • 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! – Stuart Rossiter Dec 03 '14 at 13:15
  • 1
    There’s no point in writing `new java.io.File("").toPath()` when you can use `Paths.get("")` in the first place. – Holger Mar 12 '21 at 07:11
50

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);
Kevin Sadler
  • 2,306
  • 25
  • 33
  • 1
    What does your answer add to Scott's or Glen's? – Uri Agassi Jun 20 '14 at 19:59
  • 13
    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. – Kevin Sadler Jun 21 '14 at 08:18
  • 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 Jan 17 '15 at 10:06
  • 4
    @momo the question was how to copy a file. – Kevin Sadler Jan 21 '15 at 12:01
  • 2
    There’s no need to go the `File` detour when you need a `Path`. `Files.copy(Paths.get("original.txt"), Paths.get("copy.txt"), …)` – Holger Mar 12 '21 at 07:13
28

To copy a file and save it to your destination path you can use the method below.

public void copy(File src, File dst) throws IOException {
    InputStream in = new FileInputStream(src);
    try {
        OutputStream out = new FileOutputStream(dst);
        try {
            // Transfer bytes from in to out
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
        } finally {
            out.close();
        }
    } finally {
        in.close();
    }
}
Boris Treukhov
  • 17,493
  • 9
  • 70
  • 91
Rakshi
  • 6,796
  • 3
  • 25
  • 46
  • 1
    This will work, but I don't think it's better than the other answers here? – Rup Oct 23 '13 at 14:18
  • 2
    @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 Jul 19 '14 at 09:41
  • 1
    @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. – Rup Jul 19 '14 at 10:02
  • Worked like a charm. Best solution that does not require 3rd party libraries and works on java 1.6. Thanks. – James Wierzba Apr 16 '15 at 19:36
  • @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. – user207421 May 17 '15 at 18:01
  • @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. – Rup May 18 '15 at 08:59
  • 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. – Reinstate Monica Sep 07 '18 at 16:55
24

Note that all of these mechanisms only copy the contents of the file, not the metadata such as permissions. So if you were to copy or move an executable .sh file on linux the new file would not be executable.

In order to truly a copy or move a file, ie to get the same result as copying from a command line, you actually need to use a native tool. Either a shell script or JNI.

Apparently, this might be fixed in java 7 - http://today.java.net/pub/a/today/2008/07/03/jsr-203-new-file-apis.html. Fingers crossed!

Brad at Kademi
  • 1,300
  • 8
  • 7
23

Google's Guava library also has a copy method:

public static void copy(File from,
                        File to)
                 throws IOException
Copies all the bytes from one file to another.

Warning: If to represents an existing file, that file will be overwritten with the contents of from. If to and from refer to the same file, the contents of that file will be deleted.

Parameters:from - the source fileto - the destination file

Throws: IOException - if an I/O error occurs IllegalArgumentException - if from.equals(to)

Divyesh Kanzariya
  • 3,629
  • 3
  • 43
  • 44
Andrew McKinlay
  • 2,431
  • 1
  • 24
  • 27
18

Available as standard in Java 7, path.copyTo: http://openjdk.java.net/projects/nio/javadoc/java/nio/file/Path.html http://java.sun.com/docs/books/tutorial/essential/io/copy.html

I can't believe it took them so long to standardise something so common and simple as file copying :(

Ryan
  • 205
  • 2
  • 2
7

Three possible problems with the above code:

  1. If getChannel throws an exception, you might leak an open stream.
  2. For large files, you might be trying to transfer more at once than the OS can handle.
  3. 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 (see Bug ID:5056395) – Jesse Glick Jan

Jason Braucht
  • 2,358
  • 19
  • 31
saji
  • 79
  • 1
  • 1
7

If you are in a web application which already uses Spring and if you do not want to include Apache Commons IO for simple file copying, you can use FileCopyUtils of the Spring framework.

Balaji Paulrajan
  • 613
  • 5
  • 15
6
public static void copyFile(File src, File dst) throws IOException
{
    long p = 0, dp, size;
    FileChannel in = null, out = null;

    try
    {
        if (!dst.exists()) dst.createNewFile();

        in = new FileInputStream(src).getChannel();
        out = new FileOutputStream(dst).getChannel();
        size = in.size();

        while ((dp = out.transferFrom(in, p, size)) > 0)
        {
            p += dp;
        }
    }
    finally {
        try
        {
            if (out != null) out.close();
        }
        finally {
            if (in != null) in.close();
        }
    }
}
user3200607
  • 107
  • 1
  • 2
6

Here is three ways that you can easily copy files with single line of code!

Java7:

java.nio.file.Files#copy

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

Appache Commons IO:

FileUtils#copyFile

private static void copyFileUsingApacheCommonsIO(File source, File dest) throws IOException {
    FileUtils.copyFile(source, dest);
}

Guava :

Files#copy

private static void copyFileUsingGuava(File source,File dest) throws IOException{
    Files.copy(source,dest);          
}
JaskeyLam
  • 15,405
  • 21
  • 114
  • 149
  • 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. – mjs Jan 17 '15 at 10:07
  • 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` – Pimp Trizkit Feb 16 '16 at 21:18
3

NIO copy with a buffer is the fastest according to my test. See the working code below from a test project of mine at https://github.com/mhisoft/fastcopy

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.text.DecimalFormat;


public class test {

private static final int BUFFER = 4096*16;
static final DecimalFormat df = new DecimalFormat("#,###.##");
public static void nioBufferCopy(final File source, final File target )  {
    FileChannel in = null;
    FileChannel out = null;
    double  size=0;
    long overallT1 =  System.currentTimeMillis();

    try {
        in = new FileInputStream(source).getChannel();
        out = new FileOutputStream(target).getChannel();
        size = in.size();
        double size2InKB = size / 1024 ;
        ByteBuffer buffer = ByteBuffer.allocateDirect(BUFFER);

        while (in.read(buffer) != -1) {
            buffer.flip();

            while(buffer.hasRemaining()){
                out.write(buffer);
            }

            buffer.clear();
        }
        long overallT2 =  System.currentTimeMillis();
        System.out.println(String.format("Copied %s KB in %s millisecs", df.format(size2InKB),  (overallT2 - overallT1)));
    }
    catch (IOException e) {
        e.printStackTrace();
    }

    finally {
        close(in);
        close(out);
    }
}

private static void close(Closeable closable)  {
    if (closable != null) {
        try {
            closable.close();
        } catch (IOException e) {
            if (FastCopy.debug)
                e.printStackTrace();
        }    
    }
}

}

Tony
  • 1,458
  • 2
  • 11
  • 13
  • nice! this one is fast rather than standar java.io stream .. copying 10GB only in 160 seconds – aswzen Oct 13 '16 at 04:44
2

Fast and work with all the versions of Java also Android:

private void copy(final File f1, final File f2) throws IOException {
    f2.createNewFile();

    final RandomAccessFile file1 = new RandomAccessFile(f1, "r");
    final RandomAccessFile file2 = new RandomAccessFile(f2, "rw");

    file2.getChannel().write(file1.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, f1.length()));

    file1.close();
    file2.close();
}
user1079877
  • 9,008
  • 4
  • 43
  • 54
  • 1
    Not all filesystems support memory mapped files though, and I think it's relatively expensive for small files. – Rup Nov 14 '13 at 10:48
  • Doesn't work with any version of Java prior to 1.4, and there is nothing that guarantees a single write is sufficient. – user207421 Jul 19 '14 at 09:44
1

A little late to the party, but here is a comparison of the time taken to copy a file using various file copy methods. I looped in through the methods for 10 times and took an average. File transfer using IO streams seem to be the worst candidate:

Comparison of file transfer using various methods

Here are the methods:

private static long fileCopyUsingFileStreams(File fileToCopy, File newFile) throws IOException {
    FileInputStream input = new FileInputStream(fileToCopy);
    FileOutputStream output = new FileOutputStream(newFile);
    byte[] buf = new byte[1024];
    int bytesRead;
    long start = System.currentTimeMillis();
    while ((bytesRead = input.read(buf)) > 0)
    {
        output.write(buf, 0, bytesRead);
    }
    long end = System.currentTimeMillis();

    input.close();
    output.close();

    return (end-start);
}

private static long fileCopyUsingNIOChannelClass(File fileToCopy, File newFile) throws IOException
{
    FileInputStream inputStream = new FileInputStream(fileToCopy);
    FileChannel inChannel = inputStream.getChannel();

    FileOutputStream outputStream = new FileOutputStream(newFile);
    FileChannel outChannel = outputStream.getChannel();

    long start = System.currentTimeMillis();
    inChannel.transferTo(0, fileToCopy.length(), outChannel);
    long end = System.currentTimeMillis();

    inputStream.close();
    outputStream.close();

    return (end-start);
}

private static long fileCopyUsingApacheCommons(File fileToCopy, File newFile) throws IOException
{
    long start = System.currentTimeMillis();
    FileUtils.copyFile(fileToCopy, newFile);
    long end = System.currentTimeMillis();
    return (end-start);
}

private static long fileCopyUsingNIOFilesClass(File fileToCopy, File newFile) throws IOException
{
    Path source = Paths.get(fileToCopy.getPath());
    Path destination = Paths.get(newFile.getPath());
    long start = System.currentTimeMillis();
    Files.copy(source, destination, StandardCopyOption.REPLACE_EXISTING);
    long end = System.currentTimeMillis();

    return (end-start);
}

The only drawback what I can see while using NIO channel class is that I still can't seem to find a way to show intermediate file copy progress.

Vinit Shandilya
  • 1,643
  • 5
  • 24
  • 44