Pregunta sobre file, copy, java – ¿Manera concisa estándar para copiar un archivo en Java?

413

Siempre me ha molestado que la única forma de copiar un archivo en Java sea abrir flujos, declarar un búfer, leer un archivo, recorrerlo y escribirlo en el otro. La web está llena de implementaciones similares, aunque todavía ligeramente diferentes, de este tipo de solución.

¿Hay una mejor manera de permanecer dentro de los límites del lenguaje Java (lo que significa que no implica ejecutar comandos específicos del sistema operativo)? ¿Quizás en algún paquete de utilidad de código abierto confiable, que al menos oscurezca esta implementación subyacente y proporcione una solución de una sola línea?

Si usa Java 7, use Files.copy en su lugar, como lo recomienda @GlenBest:stackoverflow.com/a/16600787/44737 rob
Podría haber algo en Apache CommonsFileUtils, Concretamente, lacopiar archivo metodos toolkit

Tu respuesta

16   la respuesta
19

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

No puedo creer que les tomó tanto tiempo estandarizar algo tan común y simple como la copia de archivos :(

Tu enlace es 404 hombre. samuel owino
No hay Path.copyTo; es Files.copy. Jesse Glick
271

Apache Commons IO es el camino a seguir, específicamenteFileUtils.copiar archivo(); se encarga de todo el trabajo pesado para usted.

Y como posdata, tenga en cuenta que las versiones recientes de FileUtils (como la versión 2.0.1) han agregado el uso de NIO para copiar archivos;NIO puede aumentar significativamente el rendimiento de copia de archivos, en gran parte debido a que las rutinas NIO aplazan la copia directamente al sistema operativo / sistema de archivos en lugar de manejarla leyendo y escribiendo bytes a través de la capa Java. Por lo tanto, si está buscando rendimiento, podría valer la pena comprobar que está utilizando una versión reciente de FileUtils.

Esto también es muy rápido, reemplacé una sección de código que estábamos ejecutando xCopy para copiar algunos directorios y ha aumentado la velocidad muy bien. (Yo usé la versión del repositorio) drye
No tengo ni idea ... He rastreado todas las fuentes de información pública que puedo encontrar, y no hay ninguna mención de una posible fecha de lanzamiento. Sin embargo, el sitio de JIRA muestra solo cinco temas abiertos, ¿quizás pronto? delfuego
Lanzamiento público de Apache Commons IO aún en 1.4, grrrrrrr Peter
Una advertencia para la gente de Android: esto NO está incluido en las API estándar de Android IlDan
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();
        }
    }
}
Entonces, ¿la diferencia con la respuesta principal aceptada es que tienes la transferencia desde un bucle while? Rup
Ni siquiera compila, y la llamada a createNewFile () es redundante y desperdiciadora. user207421
1

pero aquí hay una comparación del tiempo que se tarda en copiar un archivo utilizando varios métodos de copia de archivos. Hice un bucle a través de los métodos por 10 veces y tomé un promedio. La transferencia de archivos utilizando flujos de IO parece ser el peor candidato:

Aquí están los 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);
}

El único inconveniente que puedo ver al usar la clase de canal NIO es que todavía no puedo encontrar una manera de mostrar el progreso de la copia intermedia de archivos.

22

método de copia:

public static void copy(File from,
                        File to)
                 throws IOException
Copia todos los bytes de un archivo a otro.

Advertencia: Sito representa un archivo existente, ese archivo se sobrescribirá con el contenido defrom. Sito yfrom referirse amismo archivo, el contenido de ese archivo será eliminado.

Parámetros:from - el archivo fuenteto - el archivo de destino

Tiros <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> - si se produce un error 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> - Sifrom.equals(to)

7

Si getChannel lanza una excepción, puede filtrar una secuencia abierta.Para archivos grandes, es posible que esté intentando transferir más de una vez de lo que el sistema operativo puede manejar.Está ignorando el valor de retorno de transferFrom, por lo que podría estar copiando solo una parte del archivo.

Esta es la razón pororg.apache.tools.ant.util.ResourceUtils.copyResource es tan complicado También tenga en cuenta que si bien transferFrom está bien, transferTo rompe en JDK 1.4 en Linux (consulteID de error: 5056395) - Jesse Glick Jan

3

igo de trabajo a continuación de un proyecto de prueba mío enhttps://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();
        }    
    }
}

}

¡agradable! Esta es más rápida que la transmisión java.io estándar. Copia 10 GB solo en 160 segundos aswzen
7

sola línea 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);
}

Guayaba :

Archivos # copia

private static void copyFileUsingGuava(File source,File dest) throws IOException{
    Files.copy(source,dest);          
}
El primero necesita 3 parámetros.Files.copy usando solo 2 parametros es paraPath aStream. Solo agrega el parámetroStandardCopyOption.COPY_ATTRIBUTES oStandardCopyOption.REPLACE_EXISTING paraPath aPath Pimp Trizkit
El primero no funciona para directorios. Maldita sea, todos se están equivocando. Más de una comunicación de la API es tu culpa. Yo también lo entendí mal. momomo
86
Estos métodos están diseñados para el rendimiento (se integran con el sistema operativo I / O nativo).Estos métodos funcionan con archivos, directorios y enlaces.Cada una de las opciones suministradas puede quedar fuera, son opcionales.La clase de utilidad
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 un directorio o archivo
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 un directorio o archivo
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 un directorio o archivo 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 );
El nombre del paquete para Archivos fue incorrecto (debe ser java.nio.file no java.nio). He enviado una edición para eso; Espero que esté bien! Stuart Rossiter
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();
}
Sin embargo, no todos los sistemas de archivos admiten archivos asignados en memoria, y creo que es relativamente caro para archivos pequeños. Rup
No funciona con ninguna versión de Java anterior a la 1.4, y no hay nada que garantice que una sola escritura sea suficiente. user207421
36

En Java 7 es fácil ...

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

Files.copy(src.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
¿Qué agrega tu respuesta a la de Scott o Glen? Uri Agassi
Es conciso, menos es más. Sus respuestas son buenas y detalladas, pero las extrañé al mirarlas. Desafortunadamente, hay muchas respuestas a esto y muchas de ellas son largas, obsoletas y complicadas, y las buenas respuestas de Scott y Glen se perdieron en eso (daré votos para ayudar con eso). Me pregunto si mi respuesta podría mejorarse reduciéndola a tres líneas eliminando la existencia () y el mensaje de error. Kevin Sadler
@momo la pregunta era cómo copiar un archivo. Kevin Sadler
Esto no funciona para los directorios. Maldita sea, todos se están equivocando. Más de una comunicación de la API es su culpa. Yo también lo entendí mal. momomo
24

l archivo, no los metadatos, como los permisos. Entonces, si tuviera que copiar o mover un archivo .sh ejecutable en linux, el nuevo archivo no sería ejecutable.

Para realmente copiar o mover un archivo, es decir, para obtener el mismo resultado que al copiar desde una línea de comando, realmente necesita usar una herramienta nativa. Ya sea un script de shell o JNI.

Aparentemente, esto podría ser arreglado en java 7 -http://today.java.net/pub/a/today/2008/07/03/jsr-203-new-file-apis.html. ¡Dedos cruzados!

179

puedes usar la siguiente sintaxis de prueba de recursos:

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

O, mejor aún, esto también se puede lograr usando la nueva clase de Archivos introducida en Java 7:

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

Bastante elegante, ¿eh?

Es increíble que Java no haya agregado cosas como esta antes de hoy. Ciertas operaciones son simplemente lo esencial de la escritura de software de computadora. Los desarrolladores de Oracle de Java podrían aprender una o dos cosas de los sistemas operativos, observando qué servicios ofrecen, para que sea MÁS FÁCIL que los novatos puedan migrar. Rick Hodgin
¡Ah gracias! No estaba al tanto de la nueva clase de "Archivos" con todas sus funciones auxiliares. Tiene exactamente lo que necesito. Gracias por el ejemplo. ChrisCantrell
@Scott: Pete pidió una solución de una línea y está tan cerca ... no es necesario envolver Files.copy en un método copyFile. Acabo de poner Files.copy (Path from, Path to) al principio de su respuesta y menciono que puede usar File.toPath () si tiene objetos File existentes: Files.copy (fromFile.toPath (), toFile.toPath ()) rob
En cuanto al rendimiento, Java NIO FileChannel es mejor, lea este artículojournaldev.com/861/4-ways-to-copy-file-in-java Pankaj
Este código tiene unamayor problema. transferTo () debe llamarse en un bucle. No garantiza transferir la totalidad del importe solicitado. user207421
28

puede utilizar el siguiente método.

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();
    }
}
Trabajado como un encanto. La mejor solución que no requiere bibliotecas de terceros y funciona en java 1.6. Gracias. James Wierzba
Este código es incorrecto.InputStream.read Puede devolver 0 incluso si hay más datos.InputStream.read Devuelve -1 cuando no hay más datos. Así que el bucle debería terminar cuando devuelve -1, no 0. Solomonoff's Secret
@EJP OK, pero no es muy inteligente. La copia de archivos debe ser una operación del sistema operativo o del sistema de archivos, no una operación de la aplicación: es de esperar que Java pueda detectar una copia y convertirla en una operación del sistema operativo, excepto al leer explícitamente el archivo. Si no cree que Java pueda hacer eso, ¿confiaría en él para optimizar las lecturas de 1K y escribir en bloques más grandes? Y si la fuente y el destino se encontraban en un recurso compartido remoto a través de una red lenta, esto claramente está haciendo un trabajo innecesario. Sí, algunos JAR de terceros son estúpidamente grandes (¡Guayaba!) Pero agregan muchas cosas como esta hechas correctamente. Rup
@EJP Por "correcto" quise decir 1) usando las mejores API disponibles, por ejemplo. donde sea posible, las nuevas IO 2) presentando las llamadas a Java de manera que pueda reconocer fácilmente que se trata de una copia de archivo, por ejemplo, canales que representan archivos completos para leer y escribir, como en otras respuestas aquí, y convertirlos en una sola llamada del sistema operativo. Creo que este enfoque es tan lento como es probable que obtengas, especialmente con un pequeño búfer. Rup
7

ache Commons IO para copiar archivos de manera simple, puede usarFileCopyUtils del marco de primavera.

276

a operación simplista y está integrada en el JDK en el nuevo paquete NIO. Ya estaba vinculado en una respuesta anterior, pero el método clave en la API de NIO son las nuevas funciones "transferTo" y "transferFrom".

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

Uno de los artículos vinculados muestra una gran manera de cómo integrar esta función en su código, usando la transferencia desde:

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 a NIO puede ser un poco complicado, por lo que es posible que desee simplemente confiar en esta mecánica antes de ir y tratar de aprender NIO de la noche a la mañana Desde la experiencia personal, puede ser muy difícil de aprender si no tiene la experiencia y fue introducido a IO a través de las transmisiones java.io.

Tres posibles problemas con el código anterior: (a) si getChannel lanza una excepción, puede filtrar una secuencia abierta; (b) para archivos grandes, podría estar intentando transferir más de una vez de lo que el sistema operativo puede manejar; (c) está ignorando el valor de retorno de transferFrom, por lo que podría estar copiando solo una parte del archivo. Esta es la razón por la que org.apache.tools.ant.util.ResourceUtils.copyResource es tan complicado. También tenga en cuenta que mientras transferFrom está bien, transferTo rompe en JDK 1.4 en Linux:bugs.sun.com/bugdatabase/view_bug.do?bug_id=5056395 Jesse Glick
En Windows Server, encuentro que los archivos copiados así con NIO de forma intermitente no pueden ser renombrados ni eliminados, incluso después de llamar a close () en todo. Creo que esto se debe a las asignaciones de memoria que NIO crea que requieren la recolección de basura, como se describe enesta publicación. foz
@Pete no parece que Apache Commons use nio todavía para copiar archivos. An̲̳̳drew
Este código tiene unamayor problema. transferTo () debe llamarse en un bucle. No garantiza transferir la totalidad del importe solicitado. user207421

Preguntas relacionadas