Вопрос по java, file, copy – Стандартный краткий способ скопировать файл в Java?

413

Меня всегда беспокоило, что единственный способ скопировать файл в Java - это открыть потоки, объявить буфер, прочитать один файл, пройти через него и записать его в другой поток. Сеть изобилует похожими, но все же немного отличающимися реализациями этого типа решения.

Есть ли лучший способ, который остается в пределах языка Java (то есть не подразумевает выполнение специфичных для ОС команд)? Возможно, в каком-нибудь надежном пакете утилит с открытым исходным кодом, который, по крайней мере, затмит эту базовую реализацию и предоставит однострочное решение?

Там может быть что-то в Apache CommonsFileUtilsВ частности,copyFile методы. toolkit
Если используется Java 7, используйте вместо этого Files.copy, как рекомендует @GlenBest:stackoverflow.com/a/16600787/44737 rob

Ваш Ответ

16   ответов
86
These methods are performance-engineered (they integrate with operating system native I/O). These methods work with files, directories and links. Each of the options supplied may be left out - they are optional. The utility class
package com.yourcompany.nio;

class Files {

    static int copyRecursive(Path source, Path target, boolean prompt, CopyOptions options...) {
        CopyVisitor copyVisitor = new CopyVisitor(source, target, options).copy();
        EnumSet<FileVisitOption> fileVisitOpts;
        if (Arrays.toList(options).contains(java.nio.file.LinkOption.NOFOLLOW_LINKS) {
            fileVisitOpts = EnumSet.noneOf(FileVisitOption.class) 
        } else {
            fileVisitOpts = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
        }
        Files.walkFileTree(source[i], fileVisitOpts, Integer.MAX_VALUE, copyVisitor);
    }

    private class CopyVisitor implements FileVisitor<Path>  {
        final Path source;
        final Path target;
        final CopyOptions[] options;

        CopyVisitor(Path source, Path target, CopyOptions options...) {
             this.source = source;  this.target = target;  this.options = options;
        };

        @Override
        FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
        // before visiting entries in a directory we copy the directory
        // (okay if directory already exists).
        Path newdir = target.resolve(source.relativize(dir));
        try {
            Files.copy(dir, newdir, options);
        } catch (FileAlreadyExistsException x) {
            // ignore
        } catch (IOException x) {
            System.err.format("Unable to create: %s: %s%n", newdir, x);
            return SKIP_SUBTREE;
        }
        return CONTINUE;
    }

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
        Path newfile= target.resolve(source.relativize(file));
        try {
            Files.copy(file, newfile, options);
        } catch (IOException x) {
            System.err.format("Unable to copy: %s: %s%n", source, x);
        }
        return CONTINUE;
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
        // fix up modification time of directory when done
        if (exc == null && Arrays.toList(options).contains(COPY_ATTRIBUTES)) {
            Path newdir = target.resolve(source.relativize(dir));
            try {
                FileTime time = Files.getLastModifiedTime(dir);
                Files.setLastModifiedTime(newdir, time);
            } catch (IOException x) {
                System.err.format("Unable to copy all attributes to: %s: %s%n", newdir, x);
            }
        }
        return CONTINUE;
    }

    @Override
    public FileVisitResult visitFileFailed(Path file, IOException exc) {
        if (exc instanceof FileSystemLoopException) {
            System.err.println("cycle detected: " + file);
        } else {
            System.err.format("Unable to copy: %s: %s%n", file, exc);
        }
        return CONTINUE;
    }
}
Copying a directory or file
long bytes = java.nio.file.Files.copy( 
                 new java.io.File("<filepath1>").toPath(), 
                 new java.io.File("<filepath2>").toPath(),
                 java.nio.file.StandardCopyOption.REPLACE_EXISTING,
                 java.nio.file.StandardCopyOption.COPY_ATTRIBUTES,
                 java.nio.file.LinkOption.NOFOLLOW_LINKS);
Moving a directory or file
long bytes = java.nio.file.Files.move( 
                 new java.io.File("<filepath1>").toPath(), 
                 new java.io.File("<filepath2>").toPath(),
                 java.nio.file.StandardCopyOption.ATOMIC_MOVE,
                 java.nio.file.StandardCopyOption.REPLACE_EXISTING);
Copying a directory or file recursively
long bytes = com.yourcompany.nio.Files.copyRecursive( 
                 new java.io.File("<filepath1>").toPath(), 
                 new java.io.File("<filepath2>").toPath(),
                 java.nio.file.StandardCopyOption.REPLACE_EXISTING,
                 java.nio.file.StandardCopyOption.COPY_ATTRIBUTES
                 java.nio.file.LinkOption.NOFOLLOW_LINKS );
Неверное имя пакета для файлов (должно быть java.nio.file, а не java.nio). Я отправил правку для этого; надеюсь, что все в порядке!
276

как Apache Commons. Это упрощенная операция, встроенная в JDK в новом пакете NIO. Это уже было похоже на предыдущий ответ, но ключевой метод в API NIO - это новые функции & quot; TransferTo & quot; и "TransferFrom".

http://java.sun.com/javase/6/docs/api/java/nio/channels/FileChannel.html#transferTo(long,%20long,%20java.nio.channels.WritableByteChannel)

Одна из связанных статей демонстрирует отличный способ интеграции этой функции в ваш код с помощью метода TransferFrom:

public static void copyFile(File sourceFile, File destFile) throws IOException {
    if(!destFile.exists()) {
        destFile.createNewFile();
    }

    FileChannel source = null;
    FileChannel destination = null;

    try {
        source = new FileInputStream(sourceFile).getChannel();
        destination = new FileOutputStream(destFile).getChannel();
        destination.transferFrom(source, 0, source.size());
    }
    finally {
        if(source != null) {
            source.close();
        }
        if(destination != null) {
            destination.close();
        }
    }
}

Изучение NIO может быть немного сложнее, так что вы можете просто поверить этому механику, прежде чем уйти и попытаться изучить NIO в одночасье. Из личного опыта очень трудно понять, если у вас нет такого опыта, и вы познакомились с IO через потоки java.io.

Я считаю, что эта обновленная версия решает следующие проблемы:gist.github.com/889747
Этот код имеетmajor проблема. TransferTo () должен вызываться в цикле. Это не гарантирует перевод всей запрашиваемой суммы.
К сожалению, есть предостережения. Когда я скопировал 1,5-гигабайтный файл в Windows 7, 32-битный, он не смог отобразить файл. Мне пришлось искать другое решение.
Спасибо, полезная информация. Я бы все еще спорил о чем-то вроде Apache Commons, особенно если он использует nio (правильно) снизу; но я согласен, что важно понимать основные принципы. Peter
Три возможные проблемы с приведенным выше кодом: (a) если getChannel выдает исключение, вы можете пропустить открытый поток; (б) для больших файлов вы можете пытаться передать больше, чем может выдержать ОС; (c) вы игнорируете возвращаемое значение TransferFrom, поэтому оно может копировать только часть файла. Вот почему org.apache.tools.ant.util.ResourceUtils.copyResource так сложен. Также обратите внимание, что пока TransferFrom в порядке, TransferTo ломается на JDK 1.4 в Linux:bugs.sun.com/bugdatabase/view_bug.do?bug_id=5056395
271

FileUtils.копировать файл(); он берет на себя всю тяжелую работу за вас.

И, как постскриптум, обратите внимание, что в последних версиях FileUtils (таких как выпуск 2.0.1) добавлено использование NIO для копирования файлов;NIO может значительно повысить производительность копирования файловВо многом потому, что подпрограммы NIO откладывают копирование непосредственно в ОС / файловую систему, а не обрабатывают его, читая и записывая байты через уровень Java. Поэтому, если вы ищете производительность, возможно, стоит проверить, используете ли вы последнюю версию FileUtils.

Очень полезно - есть ли у вас понимание того, когда официальный релиз будет включать эти изменения nio? Peter
Публичный выпуск Apache Commons IO все еще на 1.4, grrrrrrr Peter
По состоянию на декабрь 2010 года Apache Commons IO имеет версию 2.0.1, которая обладает функциональностью NIO. Ответ обновлен.
Предупреждение для пользователей Android: это НЕ включено в стандартные API Android
Если вы используете Java 7 или новее, вы можете использовать Files.copy в соответствии с предложением @GlenBest:stackoverflow.com/a/16600787/44737
7

которыми вы можете легко скопировать файлы с помощью одной строки кода!

Java7:

java.nio.file.Files # копия

private static void copyFileUsingJava7Files(File source, File dest) throws IOException {
    Files.copy(source.toPath(), dest.toPath());
}

Appache Commons IO:

FileUtils # CopyFile

private static void copyFileUsingApacheCommonsIO(File source, File dest) throws IOException {
    FileUtils.copyFile(source, dest);
}

Guava :

Файлы # копировать

private static void copyFileUsingGuava(File source,File dest) throws IOException{
    Files.copy(source,dest);          
}
Сначала нужно 3 параметра.Files.copy используя только 2 параметра дляPath вStream, Просто добавьте параметрStandardCopyOption.COPY_ATTRIBUTES или жеStandardCopyOption.REPLACE_EXISTING заPath вPath
Первый из них не работает для каталогов. Черт, каждый ошибается. Больше проблем с интерфейсом API - ваша ошибка. Я тоже понял это неправильно.
6
public static void copyFile(File src, File dst) throws IOException
{
    long p = 0, dp, size;
    FileChannel in = null, out = null;

    try
    {
        if (!dst.exists()) dst.createNewFile();

        in = new FileInputStream(src).getChannel();
        out = new FileOutputStream(dst).getChannel();
        size = in.size();

        while ((dp = out.transferFrom(in, p, size)) > 0)
        {
            p += dp;
        }
    }
    finally {
        try
        {
            if (out != null) out.close();
        }
        finally {
            if (in != null) in.close();
        }
    }
}
Даже не компилируется, и вызов createNewFile () является избыточным и расточительным.
Таким образом, отличие от принятого сверху ответа состоит в том, что вы получили TransferFrom в цикле while?
7

которое уже использует Spring, и если вы не хотите включать Apache Commons IO для простого копирования файлов, вы можете использоватьFileCopyUtils рамки весны.

36

File src = new File("original.txt");
File target = new File("copy.txt");

Files.copy(src.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
@ Момо вопрос был, как скопировать файл.
Что ваш ответ добавляет к Скотту или Глену?
Это не работает для каталогов. Черт, каждый ошибается. Больше проблем с интерфейсом API - ваша ошибка. Я тоже понял это неправильно.
Это сжато, чем меньше, тем больше. Их ответы хорошие и подробные, но я скучал по ним при просмотре. К сожалению, есть много ответов на это, и многие из них являются длинными, устаревшими и сложными, и хорошие ответы Скотта и Глена были потеряны в этом (я предоставлю голосование, чтобы помочь с этим). Интересно, можно ли улучшить мой ответ, сократив его до трех строк, выбив существующий () и сообщение об ошибке.
3

ниже из моего тестового проекта наhttps://github.com/mhisoft/fastcopy

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.text.DecimalFormat;


public class test {

private static final int BUFFER = 4096*16;
static final DecimalFormat df = new DecimalFormat("#,###.##");
public static void nioBufferCopy(final File source, final File target )  {
    FileChannel in = null;
    FileChannel out = null;
    double  size=0;
    long overallT1 =  System.currentTimeMillis();

    try {
        in = new FileInputStream(source).getChannel();
        out = new FileOutputStream(target).getChannel();
        size = in.size();
        double size2InKB = size / 1024 ;
        ByteBuffer buffer = ByteBuffer.allocateDirect(BUFFER);

        while (in.read(buffer) != -1) {
            buffer.flip();

            while(buffer.hasRemaining()){
                out.write(buffer);
            }

            buffer.clear();
        }
        long overallT2 =  System.currentTimeMillis();
        System.out.println(String.format("Copied %s KB in %s millisecs", df.format(size2InKB),  (overallT2 - overallT1)));
    }
    catch (IOException e) {
        e.printStackTrace();
    }

    finally {
        close(in);
        close(out);
    }
}

private static void close(Closeable closable)  {
    if (closable != null) {
        try {
            closable.close();
        } catch (IOException e) {
            if (FastCopy.debug)
                e.printStackTrace();
        }    
    }
}

}

отлично! этот быстрый, а не стандартный поток java.io .. копирование 10 ГБ всего за 160 секунд
1

но вот сравнение времени, затрачиваемого на копирование файла с использованием различных методов копирования файлов. Я перебрал методы 10 раз и набрал среднее. Передача файлов с использованием потоков ввода-вывода кажется худшим кандидатом:

Comparison of file transfer using various methods

Вот методы:

private static long fileCopyUsingFileStreams(File fileToCopy, File newFile) throws IOException {
    FileInputStream input = new FileInputStream(fileToCopy);
    FileOutputStream output = new FileOutputStream(newFile);
    byte[] buf = new byte[1024];
    int bytesRead;
    long start = System.currentTimeMillis();
    while ((bytesRead = input.read(buf)) > 0)
    {
        output.write(buf, 0, bytesRead);
    }
    long end = System.currentTimeMillis();

    input.close();
    output.close();

    return (end-start);
}

private static long fileCopyUsingNIOChannelClass(File fileToCopy, File newFile) throws IOException
{
    FileInputStream inputStream = new FileInputStream(fileToCopy);
    FileChannel inChannel = inputStream.getChannel();

    FileOutputStream outputStream = new FileOutputStream(newFile);
    FileChannel outChannel = outputStream.getChannel();

    long start = System.currentTimeMillis();
    inChannel.transferTo(0, fileToCopy.length(), outChannel);
    long end = System.currentTimeMillis();

    inputStream.close();
    outputStream.close();

    return (end-start);
}

private static long fileCopyUsingApacheCommons(File fileToCopy, File newFile) throws IOException
{
    long start = System.currentTimeMillis();
    FileUtils.copyFile(fileToCopy, newFile);
    long end = System.currentTimeMillis();
    return (end-start);
}

private static long fileCopyUsingNIOFilesClass(File fileToCopy, File newFile) throws IOException
{
    Path source = Paths.get(fileToCopy.getPath());
    Path destination = Paths.get(newFile.getPath());
    long start = System.currentTimeMillis();
    Files.copy(source, destination, StandardCopyOption.REPLACE_EXISTING);
    long end = System.currentTimeMillis();

    return (end-start);
}

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

22

метод копирования:

public static void copy(File from,
                        File to)
                 throws IOException
Copies all the bytes from one file to another.

Warning: If to represents an existing file, that file will be overwritten with the contents of from. If to and from refer to the same file, the contents of that file will be deleted.

Parameters:from - the source fileto - the destination file

Throws: IOException - if an I/O error occurs IllegalArgumentException - if from.equals(to)

19

http://openjdk.java.net/projects/nio/javadoc/java/nio/file/Path.html http://java.sun.com/docs/books/tutorial/essential/io/copy.html

Я не могу поверить, что им потребовалось так много времени, чтобы стандартизировать что-то такое простое и простое, как копирование файлов :(

Ваша ссылка 404 человек
Нет Path.copyTo; это Files.copy.
2

private void copy(final File f1, final File f2) throws IOException {
    f2.createNewFile();

    final RandomAccessFile file1 = new RandomAccessFile(f1, "r");
    final RandomAccessFile file2 = new RandomAccessFile(f2, "rw");

    file2.getChannel().write(file1.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, f1.length()));

    file1.close();
    file2.close();
}
Однако не все файловые системы поддерживают файлы с отображением в памяти, и я думаю, что это относительно дорого для небольших файлов.
Не работает ни с какой версией Java до 1.4, и нет ничего, что гарантировало бы, что одной записи достаточно.
24

что все эти механизмы копируют только содержимое файла, а не метаданные, такие как разрешения. Таким образом, если вы скопируете или переместите исполняемый файл .sh в Linux, новый файл не будет исполняемым.

Чтобы действительно скопировать или переместить файл, то есть получить тот же результат, что и при копировании из командной строки, вам действительно нужно использовать встроенный инструмент. Либо сценарий оболочки, либо JNI.

По-видимому, это может быть исправлено в Java 7 -http://today.java.net/pub/a/today/2008/07/03/jsr-203-new-file-apis.html, Скрещенные пальцы!

7

If getChannel throws an exception, you might leak an open stream. For large files, you might be trying to transfer more at once than the OS can handle. You are ignoring the return value of transferFrom, so it might be copying just part of the file.

Вот почемуorg.apache.tools.ant.util.ResourceUtils.copyResource это так сложно. Также обратите внимание, что пока TransferFrom в порядке, TransferTo ломается на JDK 1.4 в Linux (см.ID ошибки: 5056395) & # x2013; Джесси Глик Ян

179

source:

public static void copyFile( File from, File to ) throws IOException {

    if ( !to.exists() ) { to.createNewFile(); }

    try (
        FileChannel in = new FileInputStream( from ).getChannel();
        FileChannel out = new FileOutputStream( to ).getChannel() ) {

        out.transferFrom( in, 0, in.size() );
    }
}

Или, что еще лучше, это также может быть достигнуто с помощью нового класса Files, представленного в Java 7:

public static void copyFile( File from, File to ) throws IOException {
    Files.copy( from.toPath(), to.toPath() );
}

Довольно шикарно, а?

с точки зрения производительности, Java NIO FileChannel лучше, прочитайте эту статьюjournaldev.com/861/4-ways-to-copy-file-in-java
Этот код имеетmajor проблема. TransferTo () должен вызываться в цикле. Это не гарантирует перевод всей запрашиваемой суммы.
Это удивительная Java, которая до сегодняшнего дня не добавляла подобные вещи. Некоторые операции - это просто абсолютные основы написания компьютерного программного обеспечения. Разработчики Oracle на Java могли бы кое-что узнать из операционных систем, посмотрев, какие услуги они предоставляют, чтобы упростить миграцию новичков.
Ах, спасибо! Я не знал о новых "Файлах" класс со всеми его вспомогательными функциями. Это именно то, что мне нужно. Спасибо за пример.
@ Скотт: Пит попросил однострочное решение, и вы так близко ... нет необходимости оборачивать Files.copy методом copyFile. Я просто поместил Files.copy (Path from, Path to) в начало вашего ответа и упомянул, что вы можете использовать File.toPath (), если у вас есть объекты File: Files.copy (fromFile.toPath (), toFile.toPath ())
28

вы можете использовать метод ниже.

public void copy(File src, File dst) throws IOException {
    InputStream in = new FileInputStream(src);
    try {
        OutputStream out = new FileOutputStream(dst);
        try {
            // Transfer bytes from in to out
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
        } finally {
            out.close();
        }
    } finally {
        in.close();
    }
}
Работал как шарм. Лучшее решение, которое не требует сторонних библиотек и работает на Java 1.6. Благодарю.
Это будет работать, но я не думаю, что это лучше, чем другие ответы здесь?
Работает с Java 6, без лишних банок.
@Rup Это значительно лучше, чем другие ответы здесь, (а)because это работает, и (б) потому что оно не полагается на стороннее программное обеспечение.
@EJP Хорошо, но это не очень умно. Копирование файлов должно быть операцией операционной системы или файловой системы, а не операцией приложения: надеюсь, Java сможет определить копию и превратить ее в операцию операционной системы, за исключением того, что вы явно прочитаете файл, который вы сделали, и прекратили это делать. Если вы не думаете, что Java может это сделать, поверите ли вы этому, чтобы оптимизировать чтение и запись 1K в большие блоки? И если источник и пункт назначения находились на удаленном общем ресурсе по медленной сети, то это явно делает ненужную работу. Да, некоторые сторонние JAR-файлы тупо большие (Guava!), Но они действительно добавляют много таких вещей, которые сделаны правильно.

Похожие вопросы