Стандартный краткий способ копирования файла на Java?

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

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

16 ответов


Как упоминалось выше в toolkit, Apache Commons IO-это путь, в частности пакета fileutils.copyFile (); он обрабатывает всю тяжелую работу для вас.

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


Я бы избегал использования mega api, такого как Apache commons. Это упрощенная операция и ее встроенный в JDK в новом пакете NIO. Это было уже связано с предыдущим ответом, но ключевым методом в api NIO являются новые функции "transferTo" и "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.потоки ввода-вывода.


теперь с Java 7 Вы можете использовать следующий синтаксис try-with-resource:

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

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

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

довольно эффектно, а?


  • эти методы проектированы представлени - (они интегрируют с I / O операционной системы родным).
  • эти методы работы с файлами, каталогами и ссылками.
  • каждый из предложенных вариантов может быть оставлен вне-они опционные.

служебный класс

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

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

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

перемещение каталога или файла

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

копирование каталога или файла рекурсивно

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 7 это легко...

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

Files.copy(src.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);

скопировать файл и сохранить его на вашем пути вы можете использовать метод, приведенный ниже.

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

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

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

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


библиотека Guava Google также имеет способ копирование:

public static void copy(File from,
                        File to)
                 throws IOException
Копирует все байты из одного файла в другой.

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

параметры:from - файл источникto - целевой файл

Броски: IOException - при возникновении ошибки ввода-вывода IllegalArgumentException - если from.equals(to)


доступно как стандарт в Java 7, путь.метод CopyTo: http://openjdk.java.net/projects/nio/javadoc/java/nio/file/Path.html http://java.sun.com/docs/books/tutorial/essential/io/copy.html

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


три возможные проблемы с выше код:

  1. если getChannel выдает исключение, вы можете пропустить Открытый поток.
  2. для больших файлов вы можете пытаться передавать больше сразу, чем может обрабатывать ОС.
  3. вы игнорируете возвращаемое значение transferFrom, поэтому оно может копировать только часть файла.

вот почему org.apache.tools.ant.util.ResourceUtils.copyResource - Это так сложно. Также отметим, что пока transferFrom ОК, трансферто перерывы на JDK 1.4 в Linux (см. ID ошибки:5056395) – Джесси Глик Января


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


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

Java7:

java.НИО.файл.Файлы#копия

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

гуавы :

файлы#копия

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

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

быстро и работать со всеми версиями Java также Android:

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

копия NIO с буфером является самой быстрой в соответствии с моим тестом. См. рабочий код ниже из моего тестового проекта в 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();
        }    
    }
}

}


немного поздно на вечеринку, но вот сравнение времени, затраченного на копирование файла с помощью различных методов копирования файлов. Я зациклился на методах в течение 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, заключается в том, что я все еще не могу найти способ показать промежуточный файл копировать прогресс.