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

我实现了文件操作function,我注意到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); 

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

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

  1. 使用内置的标准库方法(例如File.renameTo()Files.move() )。
  2. 通过将字节从源复制到目标来自己完成工作。

首先,请注意File没有复制方法,因此当您谈论复制时,只有一个选项可用于内置标准库方法。

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

所以你有两种情况:

重命名

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

File是一个过时的类。 它不应该再被使用了。 有一个很好的解释为什么 ,这总结了File在任何标准方法失败时没有给你任何信息这一事实,而Files在发生这种情况时会为你提供非常准确的exception。

仿形

您有两种选择 – 使用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复制到文件的选项,因此如果复制的一端是流而另一端是文件,则可以使用它。 它将是可读的,安全的,并抛出有意义的exception。

所以写自己的副本:

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

这里已经讨论得足够了 ,以下是从这里开始的

您的第一种方法是文件重命名,与文件副本无关

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

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

2.使用java.nio.channels.FileChannel:在Java 1.4中引入了Java NIO类,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,因此如果您尚未将其用于其他function,则可以避免使用此包装方法。

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是复制大文件的最佳方式。 如果您使用更大的文件,您会发现速度差异更大