Cómo copiar un archivo en Java

Copiar un archivo o directorio solía ser una tarea típica de desarrollo. Con la introducción de los contenedores Docker y el deseo de máxima inmutabilidad, lo vemos...

Copiar archivos en Java

Copiar un archivo o directorio solía ser una tarea típica de desarrollo. Con la introducción de los contenedores Docker y el deseo de máxima inmutabilidad, lo vemos cada vez con menos frecuencia.

Aún así, es un concepto fundamental, y puede ser útil saber qué opciones tiene el desarrollador cuando necesita copiar un archivo.

Flujos de E/S

Antes de Java 1.5, la forma estándar de copiar un archivo era utilizando Flujos de E/S:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
private void copyFile(File src, File dest) throws IOException {
    InputStream inputStream = null;
    OutputStream outputStream = null;
    try {
        inputStream = new FileInputStream(source);
        outputStream = new FileOutputStream(dest);

        // the size of the buffer doesn't have to be exactly 1024 bytes, try playing around with this number and see what effect it will have on the performance
        byte[] buffer = new byte[1024];
        int length = 0;
        while ((length = is.read(buffer)) > 0) {
            os.write(buffer, 0, length);
        }
    } finally {
        is.close();
        os.close();
    }
}

Canales y java.nio

Con la importante refactorización de la plataforma Java y el lanzamiento de Java 1.4 en 2000, se introdujo el paquete java.nio.

El proceso de copia de Archivo comenzó a verse un poco más elegante y sencillo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
private void copyFileUsingChannel(File src, File dest) throws IOException {
    FileChannel sourceChannel = null;
    FileChannel destinationChannel = null;
    try {
        sourceChannel = new FileInputStream(src).getChannel();
        destinationChannel = new FileOutputStream(dest).getChannel();
        destinationChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
       } finally {
           sourceChannel.close();
           destinationChannel.close();
       }
}

La diferencia más notable es que nos deshicimos del bucle while y ya no controlamos el tamaño del búfer directamente.

Clase auxiliar de archivos

Junto con Java 7, lanzado en 2011, se agregó una clase auxiliar que nos permite copiar archivos en una sola línea de código:

1
2
3
private static void copyFile(File src, File dest) throws IOException {
    Files.copy(src.toPath(), dest.toPath());
}

Echando un vistazo a la documentación de la API, vemos que es posible proporcionar una opción adicional a la método copia().

Se admiten las siguientes opciones de las enumeraciones StandardCopyOption y LinkOption:

  • REPLACE_EXISTING – Realiza la copia incluso cuando el archivo de destino ya existe. Si el destino es un enlace simbólico, se copia el propio enlace (y no el destino del enlace). Si el destino es un directorio no vacío, la copia falla con la excepción FileAlreadyExistsException.

  • COPY_ATTRIBUTES: copia los atributos de archivo asociados con el archivo en el archivo de destino. Los atributos de archivo exactos admitidos dependen del sistema de archivos y de la plataforma, pero la hora de la última modificación se admite en todas las plataformas y se copia en el archivo de destino.

  • NOFOLLOW_LINKS – Indica que no se deben seguir los enlaces simbólicos. Si el archivo que se va a copiar es un enlace simbólico, se copia el enlace (y no el destino del enlace).

Apache Commons IO y Google Guava

Estas dos bibliotecas de ayuda muy útiles proporcionan una API muy similar para copiar archivos. El único inconveniente de usar cualquiera de ellos es tener que agregar otro archivo .jar pesado al proyecto.

Pero si ya está ahí, ¿por qué no?

1
2
3
private void copyFileApacheCommons(File src, File dest) throws IOException {
    FileUtils.copyFile(src, dest);
}
1
2
3
private void copyFileGoogleGuava(File src, File dest) throws IOException {
    Files.copy(src, dest);
}

Bonificación

Espera, pero ¿qué tengo una Cadena que representa el nombre del archivo y no el objeto del Archivo real?

No te preocupes, ¡lo tenemos cubierto!

1
2
3
private File filenameToFile(String filename) throws IOException {
    return new File(filename);
}

¿Cual deberías usar? {#Cual deberías usar}

Flujos de E/S y [Canales](https://docs.oracle.com/javase/7/docs /api/java/nio/channels/package-summary.html) son una opción viable para jugar y comprender cómo se ve el proceso desde adentro.

Otro sería si está trabajando en un proyecto heredado con una versión obsoleta de Java y actualizarlo no es una opción. En todos los demás casos, debe comprobar si Apache Commons oGoogle guayaba ya está en la lista de dependencias y elegir entre uno de esos.

Si no lo son, entonces no debe agregarlos por el bien de este único método de utilidad y usar la clase de ayuda Files de Java 7.

Licensed under CC BY-NC-SA 4.0