Files.copy: примеры (JAVA)
Files.copy(Path source, Path target): PathКраткое описание метода
Метод Files.copy из пакета java.nio.file предоставляет несколько перегрузок для копирования данных между файловой системой и потоками. Используется при копировании файлов, записи входного потока в файл или при чтении файла в выходной поток. Подходит для простых операций копирования с возможностью указать поведение при совпадении имен и при сохранении атрибутов.
Доступные перегрузки и их поведение:
- Path copy(Path source, Path target, CopyOption... options) - копирует файл или символическую ссылку source в target. Возвращает
Pathцелевого файла (обычно target). ПараметрCopyOption...принимает значения изjava.nio.file.StandardCopyOption, напримерREPLACE_EXISTINGиCOPY_ATTRIBUTES. Без опцииREPLACE_EXISTINGпри существующем target бросаетсяFileAlreadyExistsException. Копирование директории обычно не выполняется (бросается IOException). - Path copy(InputStream in, Path target, CopyOption... options) - читает данные из потока in и записывает в файл target. Возвращает
Pathцелевого файла. Опции применяются аналогично первой перегрузке (например, перезапись или копирование атрибутов). - long copy(Path source, OutputStream out) - читает данные из файла source и записывает их в out. Возвращает количество байт, переданных в выходной поток (
long). Опции для этой перегрузки не передаются.
Типичные исключения и возвраты:
IOException- общая ошибка ввода-вывода при чтении/записи.FileAlreadyExistsException- цель существует и не задана опция перезаписи.SecurityException- отсутствуют права доступа для чтения/записи.UnsupportedOperationException- запрошенные опции не поддерживаются файловой системой (например, копирование атрибутов на некоторых платформах).
Особенности: метод не выполняет рекурсивное копирование дерева каталогов. Для тонкой настройки поведения лучше вручную обходить структуру файлов и применять Files.copy к каждому файлу.
Небольшие примеры использования
Пример 1: копирование файла в файл с перезаписью и копированием атрибутов
import java.nio.file.*;
import java.nio.file.StandardCopyOption;
Path src = Paths.get("/tmp/source.txt");
Path dst = Paths.get("/tmp/dest.txt");
Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
System.out.println("Скопировано");
Скопировано
Пример 2: запись из InputStream в файл
import java.io.*;
import java.nio.file.*;
try (InputStream in = new ByteArrayInputStream("Пример".getBytes())) {
Path target = Paths.get("/tmp/out.txt");
Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING);
System.out.println("Записан файл " + target);
}
Записан файл /tmp/out.txt
Пример 3: чтение файла в OutputStream и получение количества байт
import java.io.*;
import java.nio.file.*;
Path source = Paths.get("/tmp/source.bin");
ByteArrayOutputStream out = new ByteArrayOutputStream();
long bytes = Files.copy(source, out);
System.out.println("Скопировано байт: " + bytes);
Скопировано байт: 1024
Пример 4: попытка копирования при существующем файле без REPLACE_EXISTING
Path src = Paths.get("/tmp/a.txt");
Path dst = Paths.get("/tmp/b.txt");
Files.copy(src, dst);
java.nio.file.FileAlreadyExistsException: /tmp/b.txt
Похожие решения в Java
- FileChannel.transferTo/transferFrom - полезны для высокопроизводительного копирования больших файлов, дают контроль над позиционированием и могут быть быстрее в некоторых реализациях.
- Files.move - при необходимости перемещения файла вместо копирования; поддерживает
ATOMIC_MOVEдля атомарной замены при поддержке файловой системы. - java.io streams (FileInputStream/FileOutputStream) - позволяет реализовать кастомную логику (буферизация, прогресс, сжатие) и контролировать процесс посекторно.
- Apache Commons IO (FileUtils.copyFile и др.) - удобные утилиты с готовыми проверками и совместимостью с старыми API.
Выбор зависит от требований: для простых операций и сохранения атрибутов удобнее Files.copy, для больших файлов или тонкого контроля по производительности лучше FileChannel или специализированные библиотеки.
Аналоги в других языках
- PHP: функция
copy($src, $dst). Простая в использовании, возвращаетtrue/false. Не сохраняет атрибуты файла автоматически. - JavaScript (Node.js):
fs.copyFile(src, dest, flags, callback)и синхронныйfs.copyFileSync. Поддерживает флагfs.constants.COPYFILE_EXCLдля отказа при существующем файле. - Python: модуль
shutil-shutil.copy(src, dst),shutil.copy2(копирует метаданные),shutil.copyfileobj(потоки). - C#:
System.IO.File.Copy(sourceFileName, destFileName, overwrite). Параметр overwrite управляет перезаписью. - Go: обычно используется комбинация
io.Copy(dst, src)с открытием файлов черезos.Openиos.Create. Возвращает количество байт и ошибку. - Kotlin: использует те же API, что и Java (java.nio.file.Files.copy) или расширения для java.io.
- Lua: нет стандартной функции копирования файлов в базовой библиотеке; обычно выполняется чтение/запись в цикле или используются сторонние модули.
- SQL: в общем виде не предназначен для работы с файловой системой; в некоторых СУБД есть утилиты для загрузки/выгрузки файлов (LOAD_FILE, BULK INSERT) но это специфично.
Короткие примеры (результаты указаны условно):
Python (shutil.copy2):
import shutil
shutil.copy2('/tmp/a.txt', '/tmp/b.txt')
print('ok')
ok
Node.js (fs.copyFileSync):
const fs = require('fs');
fs.copyFileSync('/tmp/a.txt', '/tmp/b.txt');
console.log('copied');
copied
Go (io.Copy):
f1, _ := os.Open("/tmp/a.txt")
f2, _ := os.Create("/tmp/b.txt")
bytes, _ := io.Copy(f2, f1)
fmt.Println("bytes:", bytes)
bytes: 2048
Типичные ошибки и их проявления
Пример 1: попытка копирования при существующем файле без опции перезаписи. Результат - исключение FileAlreadyExistsException.
Path src = Paths.get("/tmp/a.txt");
Path dst = Paths.get("/tmp/b.txt");
Files.copy(src, dst);
java.nio.file.FileAlreadyExistsException: /tmp/b.txt at ...
Пример 2: недостаток прав на запись. Результат - AccessDeniedException или IOException.
Path src = Paths.get("/etc/passwd");
Path dst = Paths.get("/root/out.txt");
Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING);
java.nio.file.AccessDeniedException: /root/out.txt
Пример 3: попытка копирования директории. Обычно возникает IOException или специфичное исключение файловой системы.
Path dir = Paths.get("/tmp/mydir");
Path dst = Paths.get("/tmp/mydir_copy");
Files.copy(dir, dst);
java.io.IOException: Unable to copy directory
Рекомендации по отладке ошибок: проверка прав доступа, существования пути, корректности типов (файл/директория) и поддерживаемых опций файловой системы.
Изменения и история
Метод Files.copy появился в Java 7 вместе с NIO.2 и в целом сохраняет обратную совместимость. Новые версии Java не вносили значительных изменений в сигнатуры. Развитие затрагивало в основном поведение на разных реализациях файловых систем и улучшения безопасности платформы. Для сохранения метаданных применяется StandardCopyOption.COPY_ATTRIBUTES, но поддержка конкретных атрибутов зависит от ОС и реализации.
Расширенные и необычные сценарии
1) Копирование с отслеживанием прогресса и поддержкой отмены. Используется FileChannel.transferTo и периодическая проверка флага отмены.
import java.io.*;
import java.nio.channels.FileChannel;
import java.nio.file.*;
Path src = Paths.get("/tmp/large.bin");
Path tmp = Paths.get("/tmp/large.bin.part");
try (FileChannel in = FileChannel.open(src, StandardOpenOption.READ);
FileChannel out = FileChannel.open(tmp, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
long size = in.size();
long pos = 0;
while (pos < size) {
long transferred = in.transferTo(pos, Math.min(8 * 1024 * 1024, size - pos), out);
pos += transferred;
System.out.printf("Скопировано %%d/%%d\n", pos, size);
// здесь возможна проверка внешнего флага отмены
}
Files.move(tmp, Paths.get("/tmp/large.bin.copy"), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
}
Скопировано 8388608/104857600 Скопировано 16777216/104857600 ...
2) Атомарическая замена существующего файла: сначала запись во временный файл, затем переименование с ATOMIC_MOVE.
Path src = Paths.get("/tmp/source.txt");
Path tmp = Paths.get("/tmp/dest.txt.tmp");
Path dst = Paths.get("/tmp/dest.txt");
Files.copy(src, tmp, StandardCopyOption.REPLACE_EXISTING);
Files.move(tmp, dst, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
(выполнено без видимой промежуточной стадии для других процессов при поддержке FS)
3) Копирование символической ссылки как ссылки (в некоторых реализациях): в зависимости от файловой системы и передачи опций поведение может отличаться. Для универсального результата можно прочитать ссылку и создать новую ссылку вручную.
Path link = Paths.get("/tmp/mylink");
Path target = Paths.get("/tmp/mylink_copy");
if (Files.isSymbolicLink(link)) {
Path pointed = Files.readSymbolicLink(link);
Files.createSymbolicLink(target, pointed);
}
(создана новая символическая ссылка, указывающая на тот же путь)
4) Копирование с трансформацией потока: чтение, сжатие и запись в целевой файл.
import java.util.zip.GZIPOutputStream;
try (InputStream in = Files.newInputStream(Paths.get("/tmp/data.txt"));
OutputStream fout = Files.newOutputStream(Paths.get("/tmp/data.txt.gz"));
GZIPOutputStream gz = new GZIPOutputStream(fout)) {
byte[] buf = new byte[8192];
int r;
while ((r = in.read(buf)) > 0) {
gz.write(buf, 0, r);
}
}
System.out.println("Сжатие выполнено");
Сжатие выполнено
5) Копирование по сети через NIO: чтение файла в ByteBuffer и отправка через SocketChannel, на приёмной стороне запись через Files.copy (посредством OutputStream).
Примеры показывают разные сценарии: от простых копирований до атомарных замен и потоковой трансформации. При работе с большим объёмом данных рекомендуется тестировать производительность и учитывать особенности платформы.