Como fazer uma cópia de um arquivo no android?


178

No meu aplicativo, quero salvar uma cópia de um determinado arquivo com um nome diferente (que recebo do usuário)

Eu realmente preciso abrir o conteúdo do arquivo e gravá-lo em outro arquivo?

Qual é a melhor forma de fazê-lo?


Antes do Java7, acho que a resposta é sim. stackoverflow.com/questions/106770/… .
Paul Grime


verifique post
Deepak

Respostas:


327

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

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

Na API 19+, você pode usar o 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);
            }
        }
    }
}

8
Obrigado. depois de bater a cabeça, descobri que faltava permissão para gravar no armazenamento externo. agora funciona bem.
AS

8
@ mohitum007 se o arquivo não conseguir copiar, uma exceção será lançada. use um bloco try catch ao chamar o método
Nima G

9
Se uma exceção for lançada, os fluxos não serão fechados até serem coletados como lixo, e isso não é bom . Considere fechá-los finally.
Pang

1
@Pang. Você está certo. O que é pior: in / out.close () deve ser chamado ou, caso contrário, haverá um vazamento de recursos no sistema operacional subjacente, pois o GC nunca fechará os descritores de arquivos abertos. O GC não pode fechar recursos do SO externos à JVM, como descritores de arquivo ou soquetes. Aqueles deve estar sempre fechado em uma cláusula finalmente programaticamente ou uso try-with-novo recurso sintaxe: docs.oracle.com/javase/tutorial/essential/exceptions/...
earizon

4
Por favor, feche os dois Streams dentro de um finalmente; se houver uma exceção, a memória do seu stream não será coletada.
22416 Pozuelog

121

Como alternativa, você pode usar o FileChannel para copiar um arquivo. Ele pode ser mais rápido do que o método de cópia byte ao copiar um arquivo grande. Você não pode usá-lo se o seu arquivo tiver mais de 2 GB.

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

3
transferTo pode lançar uma exceção e, nesse caso, você está deixando os fluxos abertos. Assim como Pang e Nima comentaram em resposta aceita.
Viktor Bresan

Além disso, transferTo deve ser chamado dentro do loop, pois não garante que irá transferir o valor total solicitado.
Viktor Brešan 29/11

Eu tentei sua solução e ela falhou com a exceção java.io.FileNotFoundException: /sdcard/AppProj/IMG_20150626_214946.jpg: open failed: ENOENT (No such file or directory)na FileOutputStream outStream = new FileOutputStream(dst);etapa. De acordo com o texto, percebo que o arquivo não existe, então verifico e ligo dst.mkdir();se necessário, mas ainda não ajuda. Eu também tentei verificar dst.canWrite();e ele retornou false. Esta é a fonte do problema? E sim, eu tenho <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>.
Mike B.

1
@ ViktorBrešan Depois de API 19 você pode usar a gestão de recursos automática Java, definindo a entrada e saída fluxos na abertura de sua tentativatry ( FileInputStream inStream = new FileInputStream(src); FileOutputStream outStream = new FileOutputStream(dst) ) {
AlbertMarkovski

Existe alguma maneira de fazer com que esta solução publique seu progresso para onProgressUpdateque eu possa mostrá-la em uma ProgressBar? Na solução aceita, posso calcular o progresso no loop while, mas não consigo ver como fazê-lo aqui.
George Bezerra

31

Extensão Kotlin para ele

fun File.copyTo(file: File) {
    inputStream().use { input ->
        file.outputStream().use { output ->
            input.copyTo(output)
        }
    }
}

Essa é a resposta mais concisa e flexível. As respostas mais simples não conseguem explicar os URIs abertos via contentResolver.openInputStream(uri).
AjahnCharles

6
Kotlin é amor, Kotlin é vida!
Dev Aggarwal

17

Eles funcionaram bem para mim

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

13

Isso é simples no Android O (API 26), como você vê:

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

10

Pode ser tarde demais para uma resposta, mas a maneira mais conveniente é usar

FileUtilsé

static void copyFile(File srcFile, File destFile)

por exemplo, foi o que eu fiz

`

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

`


20
O FileUtils não existe nativamente no Android.
The Berga

2
Mas há uma biblioteca criada pelo Apache que faz isso e muito mais: commons.apache.org/proper/commons-io/javadocs/api-2.5/org/…
Alessandro Muzzi

7

Muito mais simples agora com Kotlin:

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

trueou falseé para substituir o arquivo de destino

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


Não é tão simples se o seu arquivo vier de uma intenção da Galeria Uri.
AjahnCharles

Leia os comentários abaixo dessa resposta: "Esta resposta é ativamente prejudicial e não merece os votos que obtém. Falha se o Uri for um conteúdo: // ou qualquer outro Uri que não seja de arquivo." (Eu fiz upvote - Eu só queria esclarecer que não é uma bala de prata)
AjahnCharles

5

Aqui está uma solução que realmente fecha os fluxos de entrada / saída se ocorrer um erro durante a cópia. Esta solução utiliza os métodos apache Commons IO IOUtils para copiar e manipular o fechamento de fluxos.

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

Parece simples
Serg Burlaka 06/04

2

no kotlin, apenas:

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

fileSrc.copyTo(fileDest)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.