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

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


当前回答

谷歌的Guava库也有一个复制方法:

公共静态无效复制(文件从, 文件) 抛出IOException 将所有字节从一个文件复制到另一个文件。

警告:如果to表示已存在的文件,则该文件 将被from的内容覆盖。如果是和 从引用同一个文件,该文件的内容 将被删除。 参数:从-源文件到-目标文件 抛出: IOException -如果发生I/O错误 IllegalArgumentException - if from.equals(to)

其他回答

如果你在一个已经使用Spring的web应用程序中,如果你不想包含Apache Commons IO来进行简单的文件复制,你可以使用Spring框架的FileCopyUtils。

现在在Java 7中,你可以使用下面的try-with-resource语法:

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

或者,更好的是,这也可以使用Java 7中引入的新Files类来完成:

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

很时髦,是吧?

虽然有点晚了,但是这里比较了使用各种文件复制方法复制一个文件所花费的时间。我循环使用这些方法10次,然后取平均值。使用IO流的文件传输似乎是最糟糕的候选者:

方法如下:

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

我在使用NIO通道类时看到的唯一缺点是,我似乎仍然无法找到一种方法来显示中间文件复制进度。

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

快速和工作与所有版本的Java和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();
}