Pergunta sobre java, file, copy – Maneira concisa padrão para copiar um arquivo em Java?

413

Sempre me incomodou que a única maneira de copiar um arquivo em Java envolve a abertura de streams, a declaração de um buffer, a leitura em um arquivo, o looping e a gravação no outro arquivo. A web está repleta de implementações semelhantes, mas ainda ligeiramente diferentes, deste tipo de solução.

Existe uma maneira melhor que permaneça dentro dos limites da linguagem Java (o que não significa executar comandos específicos do sistema operacional)? Talvez em algum pacote confiável de software de fonte aberta, isso pudesse pelo menos obscurecer essa implementação subjacente e fornecer uma solução de uma linha?

Pode haver algo no Apache CommonsFileUtilsEspecificamente, ocopyFile métodos. toolkit
Se estiver usando o Java 7, use Files.copy, conforme recomendado pelo @GlenBest:stackoverflow.com/a/16600787/44737 rob

Sua resposta

16   a resposta
1

mas aqui está uma comparação do tempo gasto para copiar um arquivo usando vários métodos de cópia de arquivos. Passei pelos métodos por 10 vezes e tirei uma média. A transferência de arquivos usando fluxos de E / S parece ser o pior candidato:

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 a classe de canal NIO é que ainda não consigo encontrar uma maneira de mostrar o progresso da cópia intermediária de arquivos.

3

eja o código de trabalho abaixo de um projeto de teste meu emhttps://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();
        }    
    }
}

}

legal! esse é um fluxo rápido em vez de standar java.io .. copiando 10 GB somente em 160 segundos aswzen
276

ração simplista e é incorporada ao JDK no novo pacote NIO. Foi uma espécie de link já 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(long,%20long,%20java.nio.channels.WritableByteChannel)

Um dos artigos vinculados mostra uma ótima maneira de integrar essa função ao seu código, usando a transferência de:

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

Aprender NIO pode ser um pouco complicado, então você pode querer apenas confiar neste mecânico antes de sair e tentar aprender NIO durante a noite. Por experiência pessoal, pode ser muito difícil aprender se você não tem experiência e foi introduzido no IO através dos fluxos java.io.

@Pete não parece que o Apache Commons use o nio ainda para copiar arquivos. An̲̳̳drew
Eu concordo que é importante entender os fundos do nio, mas eu ainda iria com o Jakarta Commons. Ravi Wallau
@ EJP Eu realmente não desenvolvo mais em Java. Você pode estar certo, então você é bem-vindo para sugerir o caminho correto. Josh
No Windows Server eu estou achando que arquivos copiados assim com o NIO intermitentemente não podem ser renomeados ou apagados, mesmo depois de chamar close () em tudo. Acredito que isso se deva aos mapeamentos de memória que o NIO cria exigindo a coleta de lixo, conformeesta postagem. foz
24

não os metadados, como permissões. Então, se você copiasse ou movesse um arquivo .sh executável no linux, o novo arquivo não seria executável.

Para realmente copiar 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. Um script de shell 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!

179

com o Java 7, você pode usar a seguinte sintaxe 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() );
    }
}

Ou, melhor ainda, isso também pode ser feito usando a nova classe Files introduzida no Java 7:

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

Muito elegante, né?

É incrível que o Java não tenha adicionado coisas assim antes de hoje. Certas operações são apenas o essencial absoluto de escrever software de computador. Os desenvolvedores de Java da Oracle poderiam aprender uma coisa ou outra sobre os sistemas operacionais, observando quais serviços eles fornecem, para facilitar a migração dos novatos. Rick Hodgin
Este código tem umprincipal problema. transferTo () deve ser chamado em um loop. Não garante a transferência de todo o valor solicitado. user207421
desempenho sábio, java NIO FileChannel é melhor, leia este artigojournaldev.com/861/4-ways-to-copy-file-in-java Pankaj
Ah obrigada! Eu não estava ciente da nova classe "Arquivos" com todas as suas funções auxiliares. Tem exatamente o que eu preciso. Obrigado pelo exemplo. ChrisCantrell
@ Scott: Pete pediu uma solução de uma linha e você está tão perto ... é desnecessário embrulhar Files.copy em um método copyFile. Eu acabei de colocar o Files.copy (caminho de, caminho para) no início de sua resposta e mencionar que você pode usar File.toPath () se você tiver objetos de arquivo existentes: Files.copy (fromFile.toPath (), toFile.toPath ()) rob
7

não quiser incluir o Apache Commons IO para copiar arquivos simples, você pode usarFileCopyUtils da estrutura da Primavera.

86
Esses métodos são projetados por engenharia de desempenho (eles se integram com a E / S nativa do sistema operacional).Esses métodos funcionam com arquivos, diretórios e links.Cada uma das opções fornecidas pode ser deixada de fora - elas são opcionais.A classe de utilidade
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;
    }
}
Copiando 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);
Movendo 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);
Copiando um diretório ou arquivo recursivamente
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 );
O nome do pacote para Files estava errado (deveria ser java.nio.file não java.nio). Eu enviei uma edição para isso; espero que esteja bem! Stuart Rossiter
271

o Apache Commons IO é o caminho a seguir, especificamenteFileUtils.copyFile (); Ele lida com todo o trabalho pesado para você.

E como um postscript, observe que as versões recentes do FileUtils (como a versão 2.0.1) adicionaram o uso do NIO para copiar arquivos;NIO pode aumentar significativamente o desempenho de cópia de arquivos, em grande parte porque as rotinas NIO adiam a cópia diretamente para o sistema operacional / sistema de arquivos, em vez de manipulá-lo lendo e escrevendo bytes através da camada Java. Portanto, se você está procurando desempenho, talvez valha a pena verificar se você está usando uma versão recente do FileUtils.

Eu não tenho idéia ... Eu vasculhei todas as fontes de informação pública que eu posso encontrar, e não há menção de uma possível data de lançamento. O site do JIRA mostra apenas cinco edições abertas, então talvez seja breve? delfuego
Isso é muito rápido também, eu substituí uma seção de código que estávamos executando o xCopy para copiar alguns diretórios e aumentou muito a velocidade. (Eu usei a versão do repositório) drye
Lançamento público do Apache Commons IO ainda em 1.4, grrrrrrr Peter
Em dezembro de 2010, o Apache Commons IO estava na versão 2.0.1, que possui a funcionalidade NIO. Responda atualizado. Simon Nickerson
36

No 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);
Isso não funciona para diretórios. Maldito todo mundo está entendendo errado. Mais de uma comunicação da API causa sua falha. Eu também entendi errado. momomo
O que sua resposta acrescenta ao de Scott ou Glen? Uri Agassi
@momo a questão era como copiar um arquivo. Kevin Sadler
É conciso, menos é mais. Suas respostas são boas e detalhadas, mas senti falta delas ao olhar. Infelizmente, há muitas respostas para isso e muitas delas são longas, obsoletas e complicadas, e as boas respostas de Scott e Glen se perderam (vou dar descontos para ajudar nisso). Gostaria de saber se a minha resposta pode ser melhorada, reduzindo-o a três linhas, eliminando o existe () e mensagem de erro. Kevin Sadler
7

Se getChannel lançar uma exceção, você poderá vazar um fluxo aberto.Para arquivos grandes, você pode estar tentando transferir mais de uma vez do que o sistema operacional pode manipular.Você está ignorando o valor de retorno de transferFrom, portanto, pode estar copiando apenas parte do arquivo.

Isso é por queorg.apache.tools.ant.util.ResourceUtils.copyResource é tão complicado. Observe também que enquanto transferFrom estiver OK, transferTo interrompe o JDK 1.4 no Linux (vejaID do erro: 5056395) - Jesse Glick Jan

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();
}
Não funciona com qualquer versão do Java anterior ao 1.4, e não há nada que garanta que uma única gravação seja suficiente. user207421
Nem todos os sistemas de arquivos suportam arquivos mapeados na memória, e eu acho que é relativamente caro para arquivos pequenos. Rup
28

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();
    }
}
@Rup É consideravelmente melhor que as outras respostas aqui, (a)Porque funciona e (b) porque não depende de software de terceiros. user207421
Funciona com o Java 6, sem frascos extras. John Henckel
Isso vai funcionar, mas eu não acho que é melhor que as outras respostas aqui? Rup
@Rup Eu concordo que deve ser uma função do sistema operacional, mas não posso fazer nenhum outro sentido do seu comentário. A parte depois do primeiro cólon está faltando um verbo em algum lugar; Eu não confiaria em '' não esperar que Java transformasse blocos de 1k em algo maior, embora eu mesmo usasse blocos muito maiores; Eu nunca iria escrever um aplicativo que usou arquivos compartilhados em primeiro lugar; e não estou ciente de que qualquer biblioteca de terceiros faz algo mais "adequado" (o que você quer dizer com isso) do que este código, exceto provavelmente usar um buffer maior. user207421
19

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 demorou tanto tempo para padronizar algo tão comum e simples como copiar arquivos :(

Não há Path.copyTo; é o Files.copy. Jesse Glick
Seu link é homem 404 samuel owino
22

método de cópia:

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

Aviso: E seto representa um arquivo existente, esse arquivo será sobrescrito com o conteúdo dofrom. E seto efrom consulte omesmo arquivo, o conteúdo desse arquivo será excluído.

Parâmetros:from - o arquivo de origemto - o arquivo de destino

Lança: <a href="http://download.oracle.com/javase/6/docs/api/java/io/IOException.html?is-external=true" rel="nofollow noreferrer" title="class or interface in java.io">IOException</a> - se ocorrer um erro de E / S<a href="http://download.oracle.com/javase/6/docs/api/java/lang/IllegalArgumentException.html?is-external=true" rel="nofollow noreferrer" title="class or interface in java.lang">IllegalArgumentException</a> - E sefrom.equals(to)

7

uma única linha de código!

Java7:

java.nio.file.Files # copy

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 :

Cópia dos arquivos #

private static void copyFileUsingGuava(File source,File dest) throws IOException{
    Files.copy(source,dest);          
}
Primeiro não funciona para diretórios. Maldito todo mundo está entendendo errado. Mais de uma comunicação da API causa sua falha. Eu também entendi errado. momomo
Primeiro precisa de 3 parâmetros.Files.copy usando apenas 2 parâmetros é paraPath paraStream. Basta adicionar o parâmetroStandardCopyOption.COPY_ATTRIBUTES ouStandardCopyOption.REPLACE_EXISTING paraPath paraPath Pimp Trizkit
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();
        }
    }
}
Nem mesmo compila, e a chamada createNewFile () é redundante e um desperdício. user207421
Então a diferença da resposta mais aceita é que você tem a transferência de um loop while? Rup

Perguntas relacionadas