¿Cómo hacer una copia de un archivo en Android?

Respuestas:

327

Para copiar un archivo y guardarlo en su ruta de destino, puede usar el siguiente método.

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

En API 19+ puedes usar Java Automatic Resource Management:

public static void copy(File src, File dst) throws IOException {
    try (InputStream in = new FileInputStream(src)) {
        try (OutputStream out = new FileOutputStream(dst)) {
            // Transfer bytes from in to out
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
        }
    }
}
Rakshi
fuente
8
Gracias. Después de golpearme la cabeza, descubrí que al problema le faltaba permiso para escribir en almacenamiento externo. Ahora funciona bien.
COMO
8
@ mohitum007 si el archivo no se copia, se genera una excepción. use un bloque try catch cuando llame al método.
Nima G
9
Si se produce una excepción, los flujos no se cerrarán hasta que se recolecte basura, y eso no es bueno . Considere cerrarlos finally.
Pang
1
@Angustia. Tienes razón. Lo que es peor, se debe llamar a in / out.close () o, de lo contrario, habrá una fuga de recursos en el sistema operativo subyacente, ya que el GC nunca cerrará los descriptores de archivos abiertos. GC no puede cerrar los recursos del sistema operativo externos a la JVM como descriptores de archivos o sockets. Esos siempre deben cerrarse en una cláusula final programáticamente o usar una nueva sintaxis de prueba con recursos: docs.oracle.com/javase/tutorial/essential/exceptions/…
earizon
44
Por favor, cierre ambas secuencias dentro de un finalmente, si hay una excepción, la memoria de sus secuencias no se recopilará.
pozuelog
121

Alternativamente, puede usar FileChannel para copiar un archivo. Se podría ser más rápido que el método de copia de bytes cuando se copia un archivo grande. Sin embargo, no puede usarlo si su archivo es más grande que 2GB.

public void copy(File src, File dst) throws IOException {
    FileInputStream inStream = new FileInputStream(src);
    FileOutputStream outStream = new FileOutputStream(dst);
    FileChannel inChannel = inStream.getChannel();
    FileChannel outChannel = outStream.getChannel();
    inChannel.transferTo(0, inChannel.size(), outChannel);
    inStream.close();
    outStream.close();
}
NuloNoname
fuente
3
transferTo puede lanzar una excepción y, en ese caso, dejará las transmisiones abiertas. Al igual que Pang y Nima comentaron en respuesta aceptada.
Viktor Brešan
Además, transferTo debe llamarse dentro del bucle ya que no garantiza que transferirá la cantidad total solicitada.
Viktor Brešan
Probé su solución y me falla con la excepción java.io.FileNotFoundException: /sdcard/AppProj/IMG_20150626_214946.jpg: open failed: ENOENT (No such file or directory)en el FileOutputStream outStream = new FileOutputStream(dst);paso. De acuerdo con el texto, me doy cuenta de que el archivo no existe, así que lo reviso y llamo dst.mkdir();si es necesario, pero todavía no ayuda. También traté de verificar dst.canWrite();y regresó false. ¿Puede esta es la fuente del problema? Y sí, tengo <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>.
Mike B.
1
@ ViktorBrešan Después de API 19, puede utilizar la gestión automática de recursos de Java definiendo las secuencias de entrada y salida en la apertura de su intentotry ( FileInputStream inStream = new FileInputStream(src); FileOutputStream outStream = new FileOutputStream(dst) ) {
AlbertMarkovski
¿Hay alguna forma de hacer que esta solución publique su progreso para onProgressUpdatepoder mostrarla en una barra de progreso? En la solución aceptada puedo calcular el progreso en el ciclo while, pero no puedo ver cómo hacerlo aquí.
George Bezerra
31

Extensión de Kotlin para ello

fun File.copyTo(file: File) {
    inputStream().use { input ->
        file.outputStream().use { output ->
            input.copyTo(output)
        }
    }
}
Dima Rostopira
fuente
Esta es la respuesta más concisa pero flexible. Las respuestas más simples no tienen en cuenta los URI abiertos a través de contentResolver.openInputStream(uri).
AjahnCharles
66
Kotlin es amor, Kotlin es vida!
Dev Aggarwal
17

Esto funcionó bien para mí

public static void copyFileOrDirectory(String srcDir, String dstDir) {

    try {
        File src = new File(srcDir);
        File dst = new File(dstDir, src.getName());

        if (src.isDirectory()) {

            String files[] = src.list();
            int filesLength = files.length;
            for (int i = 0; i < filesLength; i++) {
                String src1 = (new File(src, files[i]).getPath());
                String dst1 = dst.getPath();
                copyFileOrDirectory(src1, dst1);

            }
        } else {
            copyFile(src, dst);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public static void copyFile(File sourceFile, File destFile) throws IOException {
    if (!destFile.getParentFile().exists())
        destFile.getParentFile().mkdirs();

    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();
        }
    }
}
Bojan Kseneman
fuente
13

Esto es simple en Android O (API 26), como puede ver:

  @RequiresApi(api = Build.VERSION_CODES.O)
  public static void copy(File origin, File dest) throws IOException {
    Files.copy(origin.toPath(), dest.toPath());
  }
Mirada lasciva
fuente
10

Puede ser demasiado tarde para una respuesta, pero la forma más conveniente es usar

FileUtils's

static void copyFile(File srcFile, File destFile)

por ejemplo, esto es lo que hice

``

private String copy(String original, int copyNumber){
    String copy_path = path + "_copy" + copyNumber;
        try {
            FileUtils.copyFile(new File(path), new File(copy_path));
            return copy_path;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

``

stopBugs
fuente
20
FileUtils no existe de forma nativa en Android.
The Berga
2
Pero hay una biblioteca hecha por Apache que hace esto y más: commons.apache.org/proper/commons-io/javadocs/api-2.5/org/…
Alessandro Muzzi
7

Mucho más simple ahora con Kotlin:

 File("originalFileDir", "originalFile.name")
            .copyTo(File("newFileDir", "newFile.name"), true)

trueo falsees para sobrescribir el archivo de destino

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-file/copy-to.html

Blundell
fuente
No es tan simple si su archivo proviene de una intención de galería Uri.
AjahnCharles
Lea los comentarios a continuación que responden: "Esta respuesta es activamente dañina y no merece los votos que recibe. Falla si el Uri es un contenido: // o cualquier otro Uri que no sea de archivo". (
Voté a favor,
5

Aquí hay una solución que realmente cierra las secuencias de entrada / salida si se produce un error durante la copia. Esta solución utiliza los métodos Apache Commons IO IOUtils para copiar y manejar el cierre de flujos.

    public void copyFile(File src, File dst)  {
        InputStream in = null;
        OutputStream out = null;
        try {
            in = new FileInputStream(src);
            out = new FileOutputStream(dst);
            IOUtils.copy(in, out);
        } catch (IOException ioe) {
            Log.e(LOGTAG, "IOException occurred.", ioe);
        } finally {
            IOUtils.closeQuietly(out);
            IOUtils.closeQuietly(in);
        }
    }
SBerg413
fuente
Parece simple
Serg Burlaka
2

en kotlin, solo:

val fileSrc : File = File("srcPath")
val fileDest : File = File("destPath")

fileSrc.copyTo(fileDest)
Sodino
fuente