How to Copy a File with Java – 如何用Java复制一个文件

最后修改: 2017年 11月 23日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we’ll cover common ways of copying files in Java.

在这篇文章中,我们将介绍在Java中复制文件的常见方法。

First, we’ll use the standard IO and NIO.2 APIs, and two external libraries: commons-io and guava.

首先,我们将使用标准的IONIO.2 API,以及两个外部库。commons-ioguava

2. IO API (Before JDK7)

2.IO API(JDK7之前)

First of all, to copy a file with java.io API, we’re required to open a stream, loop through the content and write it out to another stream:

首先,java.io API复制一个文件,我们需要打开一个流,循环浏览内容并将其写入另一个流中:

@Test
public void givenIoAPI_whenCopied_thenCopyExistsWithSameContents() 
  throws IOException {
 
    File copied = new File("src/test/resources/copiedWithIo.txt");
    try (
      InputStream in = new BufferedInputStream(
        new FileInputStream(original));
      OutputStream out = new BufferedOutputStream(
        new FileOutputStream(copied))) {
 
        byte[] buffer = new byte[1024];
        int lengthRead;
        while ((lengthRead = in.read(buffer)) > 0) {
            out.write(buffer, 0, lengthRead);
            out.flush();
        }
    }
 
    assertThat(copied).exists();
    assertThat(Files.readAllLines(original.toPath())
      .equals(Files.readAllLines(copied.toPath())));
}

Quite a lot of work to implement such basic functionality.

要实现这样的基本功能需要做很多工作。

Luckily for us, Java has improved its core APIs and we have a simpler way of copying files using NIO.2 API.

幸运的是,Java已经改进了它的核心API,我们有一个更简单的方法来使用NIO.2 API复制文件

3. NIO.2 API (JDK7)

3.NIO.2 API(JDK7)

Using NIO.2 can significantly increase file copying performance since the NIO.2 utilizes lower-level system entry points.

使用NIO.2可以显著提高文件复制性能,因为NIO.2利用了较低级别的系统入口点。

Let’s take a closer look at how the Files.copy() method works.

让我们仔细看看Files.copy()方法是如何工作的。

The copy() method gives us the ability to specify an optional argument representing a copy option. By default, copying files and directories won’t overwrite existing ones, nor will it copy file attributes.

copy()方法让我们能够指定一个代表复制选项的可选参数。默认情况下,复制文件和目录不会覆盖现有文件,也不会复制文件属性。

This behavior can be changed using the following copy options:

这种行为可以通过以下复制选项来改变。

  • REPLACE_EXISTING – replace a file if it exists
  • COPY_ATTRIBUTES – copy metadata to the new file
  • NOFOLLOW_LINKS – shouldn’t follow symbolic links

The NIO.2 Files class provides a set of overloaded copy() methods for copying files and directories within the file system.

NIO.2文件类提供了一组重载的copy()方法,用于在文件系统中复制文件和目录。

Let’s take a look at an example using copy() with two Path arguments:

让我们看看一个使用copy()的例子,有两个Path参数。

@Test
public void givenNIO2_whenCopied_thenCopyExistsWithSameContents() 
  throws IOException {
 
    Path copied = Paths.get("src/test/resources/copiedWithNio.txt");
    Path originalPath = original.toPath();
    Files.copy(originalPath, copied, StandardCopyOption.REPLACE_EXISTING);
 
    assertThat(copied).exists();
    assertThat(Files.readAllLines(originalPath)
      .equals(Files.readAllLines(copied)));
}

Note that directory copies are shallow, meaning that files and sub-directories within the directory are not copied.

注意,目录复制是浅层的,意味着目录内的文件和子目录不会被复制。

4. Apache Commons IO

4.Apache Commons IO

Another common way to copy a file with Java is by using the commons-io library.

用Java复制文件的另一个常用方法是使用commons-io库。

First, we need to add the dependency:

首先,我们需要添加依赖关系。

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.11.0</version>
</dependency>

The latest version can be downloaded from Maven Central.

最新版本可以从Maven中心下载。

Then, to copy a file we just need to use the copyFile() method defined in the FileUtils class. The method takes a source and a target file.

然后,为了复制一个文件,我们只需要使用copyFile()类中定义的FileUtils方法。该方法需要一个源文件和一个目标文件。

Let’s take a look at a JUnit test using the copyFile() method:

让我们看一下使用copyFile()方法的JUnit测试。

@Test
public void givenCommonsIoAPI_whenCopied_thenCopyExistsWithSameContents() 
  throws IOException {
    
    File copied = new File(
      "src/test/resources/copiedWithApacheCommons.txt");
    FileUtils.copyFile(original, copied);
    
    assertThat(copied).exists();
    assertThat(Files.readAllLines(original.toPath())
      .equals(Files.readAllLines(copied.toPath())));
}

5. Guava

5 番石榴

Finally, we’ll take a look at Google’s Guava library.

最后,我们将看一下谷歌的Guava库。

Again, if we want to use Guava, we need to include the dependency:

同样,如果我们想使用Guava我们需要包含这个依赖关系。

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.0.1-jre</version>
</dependency>

The latest version can be found on Maven Central.

最新版本可以在Maven中心找到

And here’s the Guava’s way of copying a file:

这里是Guava复制文件的方式。

@Test
public void givenGuava_whenCopied_thenCopyExistsWithSameContents() 
  throws IOException {
 
    File copied = new File("src/test/resources/copiedWithGuava.txt");
    com.google.common.io.Files.copy(original, copied);
 
    assertThat(copied).exists();
    assertThat(Files.readAllLines(original.toPath())
      .equals(Files.readAllLines(copied.toPath())));
}

6. Conclusion

6.结论

In this article, we explored the most common ways to copy a file in Java.

在这篇文章中,我们探讨了在Java中复制文件的最常见方法。

The full implementation of this article can be found over on Github.

本文的完整实现可以在Github上找到over。