ChatGPT解决这个技术问题 Extra ChatGPT

Standard concise way to copy a file in Java?

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?

There could be something in Apache Commons FileUtils, Specifically, the copyFile methods.
If using Java 7, use Files.copy instead, as recommended by @GlenBest: stackoverflow.com/a/16600787/44737

3
3 revs, 3 users 73%

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.


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.
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.
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
I believe this updated version addresses those concerns: gist.github.com/889747
This code has a major problem. transferTo() must be called in a loop. It doesn't guarantee to transfer the entire amount requested.
S
Steve Blackwell

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.


Very helpful - do you have any insight as to when an official release will incorporate these nio changes?
Public release of Apache Commons IO still at 1.4, grrrrrrr
As of Dec 2010, Apache Commons IO is at 2.0.1, which has the NIO functionality. Answer updated.
A warning to Android people: this is NOT included in the standard Android APIs
If using Java 7 or newer, you can use Files.copy as suggested by @GlenBest: stackoverflow.com/a/16600787/44737
S
Scott

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?


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.
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.
performance wise, java NIO FileChannel is better, read this article journaldev.com/861/4-ways-to-copy-file-in-java
This code has a major problem. transferTo() must be called in a loop. It doesn't guarantee to transfer the entire amount requested.
@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())
r
randers

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 );

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!
There’s no point in writing new java.io.File("<filepath1>").toPath() when you can use Paths.get("<filepath1>") in the first place.
K
Kevin Sadler

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);

What does your answer add to Scott's or Glen's?
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.
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.
@momo the question was how to copy a file.
There’s no need to go the File detour when you need a Path. Files.copy(Paths.get("original.txt"), Paths.get("copy.txt"), …)
B
Boris Treukhov

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();
    }
}

This will work, but I don't think it's better than the other answers here?
@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.
@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.
Worked like a charm. Best solution that does not require 3rd party libraries and works on java 1.6. Thanks.
@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.
B
Brad at Kademi

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!


D
Divyesh Kanzariya

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)


R
Ryan

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 :(


There is no Path.copyTo; it is Files.copy.
J
Jason Braucht

Three possible problems with the above code:

If getChannel throws an exception, you might leak an open stream. For large files, you might be trying to transfer more at once than the OS can handle. 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


B
Balaji Paulrajan

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.


u
user3200607
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();
        }
    }
}

So the difference from the top accepted answer is that you've got the transferFrom in a while loop?
Doesn't even compile, and the createNewFile() call is redundant and wasteful.
J
JaskeyLam

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);          
}

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.
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
T
Tony

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();
        }    
    }
}

}


nice! this one is fast rather than standar java.io stream .. copying 10GB only in 160 seconds
u
user1079877

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();
}

Not all filesystems support memory mapped files though, and I think it's relatively expensive for small files.
Doesn't work with any version of Java prior to 1.4, and there is nothing that guarantees a single write is sufficient.
V
Vinit Shandilya

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:

https://i.stack.imgur.com/yM0GV.png

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.