Как копировать файлы в Java

копирование файлов Java

В этой статье мы рассмотрим типичные способы копирования файлов в Java на примере четырех библиотек: встроенных IO и NIO.2 API и внешних Commons IO и Guava.

IO API (До JDK7)

При использовании этого встроенного пакета для копирования файла необходимо открыть файловый поток, пройтись циклом по его содержимому и записать его в результирующий поток.

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

Прим. перев. При таком способе права доступа (rwx) и даты создания/модификации не копируются.

Довольно много кода для реализации базовой функции. К счастью, Java постепенно совершенствует свои встроенные библиотеки, и, начиная с JDK7, можно использовать более простой способ копирования с помощью NIO.2 API.

NIO.2 API (JDK7)

Эта библиотека улучшает производительность процедуры копирования за счет использования низкоуровневых точек входа.

По умолчанию новые скопированные файлы не могут переписать ранее созданные. Также нельзя скопировать атрибуты файла-источника. Но поведение метода Files.copy() может быть изменено при использовании специальных опций:

  • REPLACE_EXISTING — заменить файл, если он уже существовал,
  • COPY_ATTRIBUTES — скопировать метаданные в новый файл,
  • NOFOLLOW_LINKS — не переходить по символическим ссылкам.

Прим. перев. Если копируемый файл — это символическая ссылка, то при использовании опции NOFOLLOW_LINKS будет скопирована сама ссылка, а не файл, на который она указывает.

Класс NIO.2 Files предоставляет несколько перегруженных методов copy() для копирования файлов и директорий в файловой системе.

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

Важно отметить, что копирование директорий – поверхностное, то есть файлы поддиректорий не скопируются.

Apache Commons IO

Для работы с библиотекой Commons IO нужно добавить зависимость:

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

Свежую версию библиотеки можно скачать с Maven Central.

Затем используем метод copyFile(), определенный в классе FileUtils. Он принимает на вход файл-источник и файл, в который будет скопирован исходный.

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

Guava

И напоследок обратим внимание на библиотеку Guava. Для ее использования также необходимо добавить зависимость:

<dependency>
     <groupId>com.google.guava</groupId>
     <artifactId>guava</artifactId>
     <version>23.0</version>
 </dependency>

Последнюю версию библиотеки можно найти на Maven Central.

Пример работы:

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

Перевод статьи «How to copy a file with Java»