一直困扰我的是,在Java中复制文件的唯一方法包括打开流,声明缓冲区,读入一个文件,循环遍历它,并将其写入到另一个steam。网络上充斥着类似的,但仍然略有不同的这类解决方案的实现。

是否有一种更好的方法能够保持在Java语言的范围内(即不涉及执行特定于操作系统的命令)?也许在一些可靠的开源实用程序包中,它至少会模糊这个底层实现,并提供一行解决方案?


当前回答

正如上面提到的工具包,Apache Commons IO是可行的方法,特别是FileUtils.copyFile();它能帮你处理所有的重担。

作为附注,请注意FileUtils的最新版本(例如2.0.1版本)已经添加了用于复制文件的NIO;NIO可以显著提高文件复制性能,这在很大程度上是因为NIO例程直接将复制推迟到OS/文件系统,而不是通过Java层读写字节来处理它。因此,如果您正在寻找性能,可能值得检查您使用的是最新版本的FileUtils。

其他回答

这里有三种方法,您可以轻松地复制文件与一行代码!

Java7:

java.nio.file.Files #副本

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

公共汽车共享10:

文件实用程序#复制文件

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

番石榴:

文件#复制

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

我会避免使用像apache commons这样的mega api。这是一个简单的操作,它内置于新的NIO包中的JDK中。在之前的回答中已经链接到它,但是NIO api中的关键方法是新函数“transferTo”和“transferFrom”。

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

其中一篇链接的文章展示了如何使用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();
        }
    }
}

学习NIO可能有点棘手,所以在尝试一夜之间学习NIO之前,您可能想要相信这个机制。从个人经验来看,如果你没有经验,并且是通过java介绍到IO的,那么学习它可能是一件非常困难的事情。io流。

根据我的测试,带缓冲区的NIO复制是最快的。下面的工作代码来自我在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();
        }    
    }
}

}

要复制文件并将其保存到目标路径,您可以使用下面的方法。

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

请注意,所有这些机制都只复制文件的内容,而不是权限等元数据。因此,如果您要复制或移动linux上的可执行.sh文件,新文件将不能执行。

为了真正地复制或移动文件,即获得与从命令行复制相同的结果,实际上需要使用本地工具。shell脚本或JNI。

显然,这可能会在java 7 - http://today.java.net/pub/a/today/2008/07/03/jsr-203-new-file-apis.html中修复。祈祷!