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

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

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

Ответ 1

В качестве упоминаемого выше инструментария Apache Commons IO - это путь, в частности FileUtils. CopyFile(); он обрабатывает весь тяжелый подъем для вас.

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

Ответ 2

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

Ответ 3

Теперь с помощью 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() );
    }
}

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

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

Довольно шумный, а??

Ответ 4

  • Эти методы разработаны с учетом производительности (они интегрируются с операционными системами ввода-вывода операционной системы).
  • Эти методы работают с файлами, каталогами и ссылками.
  • Каждый из предоставленных опций может быть опущен - они являются необязательными.

Класс утилиты

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

Ответ 5

В Java 7 легко...

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

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

Ответ 6

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

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

Ответ 7

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

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

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

Ответ 8

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

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

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

Параметры: из - исходный файл в - файл назначения

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

Ответ 9

Доступен в качестве стандарта в 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

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

Ответ 10

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

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

Вот почему org.apache.tools.ant.util.ResourceUtils.copyResource настолько сложна. Также обратите внимание, что while transferFrom в порядке, transferTo разбивается на JDK 1.4 на Linux (см. Идентификатор ошибки: 5056395) - Джесси Глик Ян

Ответ 11

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

Ответ 12

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

Java7

java.nio.file.Files # copy

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

Appomm Commons IO:

FileUtils # copyFile

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

Гуава:

Файлы # copy

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

Ответ 13

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

Ответ 14

Быстро и работайте со всеми версиями 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();
}

Ответ 15

Копирование 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();
        }    
    }
}

}