Pergunta

Ele sempre me incomodou que a única maneira de copiar um arquivo em Java envolve fluxos de abertura, declarando um buffer, lendo em um arquivo, looping através dele, e escrevê-lo para fora para o outro vapor. A web está repleta de semelhantes e ainda assim ligeiramente diferentes implementações deste tipo de solução,.

Existe uma maneira melhor que permanece dentro dos limites da linguagem Java (significado não envolve comandos OS específicos exec-ing)? Talvez em algum pacote de serviço público aberto fonte confiável, que seria, pelo menos obscurecer essa implementação subjacente e fornecer uma solução de uma linha?

Foi útil?

Solução

Como kit de ferramentas menciona acima, Apache Commons IO é o caminho a percorrer, especificamente FileUtils . CopyFile () ; ele lida com todo o trabalho pesado para você.

E, como um pós-escrito, nota que as versões recentes do FileUtils (como o lançamento 2.0.1) ter adicionado o uso de NIO para copiar arquivos; NIO pode aumentar significativamente de cópia de arquivo desempenho, em grande parte porque as rotinas NIO Adiar copiar diretamente para o oS / sistema de arquivos em vez de segurá-lo por ler e escrever bytes através da camada de Java. Então, se você está olhando para o desempenho, pode valer a pena verificar se você está usando uma versão recente do FileUtils.

Outras dicas

Gostaria de evitar o uso de uma api de mega como o Apache commons. Esta é uma operação simples e seu embutido no JDK no novo pacote NIO. Foi meio que já ligados a em uma resposta anterior, mas o método chave na api NIO são as novas funções "transferTo" e "transferFrom".

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

Um dos shows de artigos ligados uma ótima maneira de como integrar essa função em seu código, usando o 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();
        }
    }
}

Aprendizagem NIO pode ser um pouco complicado, então você pode querer apenas confiança neste mecânico antes de sair e tentar aprender NIO durante a noite. Da experiência pessoal pode ser uma coisa muito difícil de aprender, se você não tem a experiência e foram introduzidas para IO através dos fluxos java.IO de.

Agora com Java 7, você pode usar o seguinte try-with-recurso sintaxe:

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

Ou, ainda melhor, isso também pode ser feito usando a nova classe Arquivos introduzido em Java 7:

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

snazzy Bonito, hein?

  • Esses métodos são engenharia de performance (eles se integram com sistema operacional nativo I / O).
  • Esses métodos funcionam com arquivos, diretórios e links.
  • Cada uma das opções fornecidas pode ser deixado de fora -. Eles são opcionais

A classe do utilitário

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

Copiar um diretório ou arquivo

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

Mover um diretório ou arquivo

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

Copiar um diretório ou arquivo de forma recursiva

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

Em Java 7 é fácil ...

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

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

Para copiar um arquivo e salvá-lo em seu caminho de destino você pode usar o método abaixo.

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

Note que todos esses mecanismos de copiar apenas o conteúdo do arquivo, e não a metadados, como permissões. Então, se você fosse para copiar ou mover um arquivo .sh executável em linux o novo arquivo não seria executável.

A fim de verdadeiramente uma cópia ou mover um arquivo, ou seja, para obter o mesmo resultado que copiar a partir de uma linha de comando, você realmente precisa usar uma ferramenta nativa. Ou um shell script ou JNI.

Aparentemente, isso pode ser corrigido em java 7 - http://today.java.net/pub/a/today/2008/07/03/jsr-203-new-file-apis.html . Dedos cruzados!

biblioteca Goiaba do Google também tem um copiar método :

public static void copy(File from,
                        File to)
                 throws IOException
Copia todos os bytes de um arquivo para outro.

Aviso: Se to representa um arquivo existente, esse arquivo será substituído com o conteúdo do from. Se to e from referem-se ao mesma de arquivo, o conteúdo desse arquivo será deletado.

Parâmetros: from - o fileto fonte - o arquivo de destino

Lança: IOException - se ocorrer um erro de I / O IllegalArgumentException - se from.equals(to)

Disponível como padrão em Java 7, path.copyTo: http://openjdk.java.net/projects/nio /javadoc/java/nio/file/Path.html http://java.sun.com/docs/books/tutorial /essential/io/copy.html

Eu não posso acreditar que levou tanto tempo para padronizar algo tão comum e simples quanto copiar o arquivo: (

Três possíveis problemas com o código acima:

  1. Se getChannel lança uma exceção, você pode vazar um fluxo aberto.
  2. Para arquivos grandes, você pode estar tentando transferir mais de uma vez que o sistema operacional pode manipular.
  3. Você está ignorando o valor de retorno de transferFrom, por isso pode ser copiar apenas uma parte do arquivo.

É por isso que org.apache.tools.ant.util.ResourceUtils.copyResource é tão complicado. Observe também que enquanto transferFrom é OK, breaks transferTo no JDK 1.4 no Linux (ver Bug: 5056395 ) - Jesse Glick Jan

Se você estiver em uma aplicação web que já usa Primavera e se você não quiser incluir Apache Commons IO para cópia de arquivo simples, você pode usar FileCopyUtils do framework Spring.

Aqui está três maneiras que você pode facilmente copiar arquivos com uma única linha de código!

Java7 :

java.nio.file.Files # copiar

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

Goiaba :

Arquivos # copiar

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

cópia NIO com um tampão é o mais rápido de acordo com o meu teste. Veja o código de trabalho abaixo de um projeto de teste da mina em 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();
        }    
    }
}

}

Rápido e trabalhar com todas as versões do Java também 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();
}

Um pouco atrasado para a festa, mas aqui é uma comparação do tempo necessário para copiar um arquivo usando vários métodos de cópia de arquivos. I loop através dos métodos para 10 vezes e teve uma média. A transferência de arquivos usando fluxos IO parece ser o pior candidato:

Comparação de transferência de arquivos usando vários métodos

Aqui estão os métodos:

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

A única desvantagem que eu posso ver ao usar classe canal NIO é que eu ainda não consigo encontrar uma maneira de mostrar intermediário progresso de cópia de arquivos.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top