用Java复制和移动文件,解释和比较不同的方法

时间:2015-06-29 18:36:54

标签: java performance file-io copy reliability

我实现了文件操作功能,我注意到Java提供了多种复制和移动文件的技术。您可以在下面找到代码片段,简要介绍这些方法:

方法#1:

File from = new File(src.getPath());
File to = new File(dst.getPath());

from.renameTo(to);

方法#2:

FileChannel inChannel = new FileInputStream(src).getChannel();
FileChannel outChannel = new FileOutputStream(dst).getChannel();

inChannel.transferTo(0, inChannel.size(), outChannel);

方法#3:

InputStream in = getContentResolver().openInputStream(selectedImageUri);
OutputStream out = new FileOutputStream("/sdcard/wallpapers/" + wall);

byte[] buffer = new byte[1024];
int read;

while ((read = in.read(buffer)) != -1) {
    out.write(buffer, 0, read);
}

方法#4:

import static java.nio.file.StandardCopyOption.*;

Files.copy(source, target, REPLACE_EXISTING);

所有这些方法都有效,但我无法掌握何时应该使用它们?每种方法的优缺点是什么,特别是从性能和可靠性的角度来看?当我不得不选择一种技术而不是另一种技术时,是否有任何特定的场景?

2 个答案:

答案 0 :(得分:2)

已经讨论了here,以下内容来自here

您的第一种方法是与文件复制无关的文件重命名

java.io.File类没有任何快捷方法可以将文件从源复制到目标。

<强> 1。使用Stream:这是java中传统的文件复制方式,这里我们创建两个文件,源和目标。然后我们从源创建InputStream并使用OutputStream将其写入目标文件。

<强> 2。使用java.nio.channels.FileChannel: Java NIO类在Java 1.4中引入,FileChannel可用于在java中复制文件。根据transferFrom()方法javadoc,这种复制文件的方式应该比使用Streams复制文件更快。

第3。使用Apache Commons IO: Apache Commons IO FileUtils.copyFile(File srcFile,File destFile)可用于在java中复制文件。如果您已经在项目中使用Apache Commons IO,那么使用它来简化代码是有意义的。在内部它使用Java NIO FileChannel,因此如果您尚未将其用于其他功能,则可以避免使用此包装方法。

<强> 4。 Java 7 Files类:如果您正在使用Java 7,则可以使用Files类copy()方法在java中复制文件。它使用文件系统提供程序来复制文件。

现在看看这些方法中哪一个更有效,我们将在一个简单的程序中使用它们中的每一个来复制一个大文件[1 GB]。为了避免缓存带来的任何性能提升,我们将使用四个不同的源文件和四个不同的目标文件。{参考链接中的代码}

  

FileStreams Copy所花费的时间= 127572360
   FileChannels Copy所花费的时间= 10449963
  Java7 Files Copy所花费的时间= 10808333
  Apache Commons IO Copy = 17971677

所花费的时间

从输出中可以清楚地看到,Stream Copy是在Java中复制File的最佳方式。 FileChannels是复制大文件的最佳方式。如果你使用更大的文件,你会发现更大的速度差异

答案 1 :(得分:1)

我们可以将您的四种方法分为两种类型:

  1. 使用内置标准库方法(例如File.renameTo()Files.move())。
  2. 自己完成工作 - 将字节从源复制到目标。
  3. 首先,请注意File没有复制方法,因此当您谈论复制时,只有一个内置标准库方法选项。

    另请注意,重命名时“自己做的工作”会非常糟糕 - 您要复制整个文件,然后删除旧文件。不是一个好的或有效的方法。在大多数情况下,在同一文件系统中重命名/移动只需更改文件元数据而不实际触及内容,因此使用标准库确实要好得多。

    所以你有两个案例:

    <强>重命名

    选项实际上是使用File.renameTo()Files.move()。没有必要使用流和复制数据。

    File是一个过时的课程。它不应该再被使用了。有一个很好的explanation why,它总结了File在任何标准方法失败时没有向您提供任何信息这一事实,而Files为您提供了非常准确的例外情况。发生了。

    <强>复制

    您有两种选择 - 使用Files.copy()或其中一种“自己动手”的方法。

    到目前为止,如果您要复制的是实际文件,则您的选择应为Files.copy()。没有必要重新发明轮子。它完全符合您的要求,有详细记录,您不太可能意外地引入错误。是的,这非常有效。

    Files.copy()依赖于底层的“提供者”来进行操作。这意味着有专门的供应商(或操作系统)特定的类来执行对该文件系统最有效的操作。无论是Linux文件系统还是Windows文件系统,都会针对它进行优化。甚至还有专门案例的提供商,例如zip文件,因此您可以使用Files.copy()复制zip,jar或war文件中的文件 - 如果您尝试“自己动手”的方法,这会更复杂。

    此外,Files.copy()会检查您编写“自己的”副本时可能会忘记的很多内容。例如,您是否记得检查您正在读取的文件和您要写入的文件是否不是同一个文件?这可能会造成严重的麻烦。 Files.copy()做到了。它检查权限,检查副本的目标是否是目录,依此类推。所以它非常可靠。

    那么为什么你可以选择做“自己的”呢?因为,Java是一种通用语言。您可以选择从文件中读取,即写入文件的选项,这样您就可以 编写自己的“复制”方法。这并不意味着你应该。

    请注意,在“方法#3”中,“源”文件实际上不是文件!它是从Image URI生成的,这意味着它可以是一个网络源。如果您的源不是文件,而是基于套接字,数据库BLOB,Web服务器请求等的流或通道,则您无法真正使用Files.copy()。这是您需要自己编写的地方。

    实际上,Files还具有从文件复制到OutputStream或从InputStream复制到文件的选项,因此如果副本的一侧是流而另一侧是一个文件,你可以使用它。它将是可读的,安全的,并且会引发有意义的例外。

    所以写下你自己的副本:

    • 当您需要将数据从源移动到非文件的目标时,
    • 当您需要以某种方式过滤或处理数据而不是按原样从源复制到目标时,
    • 当您在1.7之前使用旧版本的Java时。在这种情况下,渠道可能比流更好。