Comment faire une copie d'un fichier sous Android?

178

Dans mon application, je souhaite enregistrer une copie d'un certain fichier avec un nom différent (que je reçois de l'utilisateur)

Ai-je vraiment besoin d'ouvrir le contenu du fichier et de l'écrire dans un autre fichier?

Quelle est la meilleure façon de le faire?

COMME
la source
Avant Java7, je pense que la réponse est oui. stackoverflow.com/questions/106770/… .
Paul Grime
vérifier le post
Deepak

Réponses:

327

Pour copier un fichier et l'enregistrer dans votre chemin de destination, vous pouvez utiliser la méthode ci-dessous.

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

Sur l'API 19+, vous pouvez utiliser la gestion automatique des ressources Java:

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
la source
8
Je vous remercie. après m'être cogné la tête, j'ai trouvé que le problème était le manque de permission d'écrire sur le stockage externe. maintenant ça marche bien.
COMME
8
@ mohitum007 si la copie du fichier échoue, une exception est levée. utilisez un bloc try catch lors de l'appel de la méthode.
Nima G
9
Si une exception est levée, les flux ne seraient pas fermés tant qu'ils n'ont pas été récupérés, et ce n'est pas bon . Pensez à les fermer finally.
Pang
1
@Serrement. Vous avez raison. Pire encore, in / out.close () doit être appelé ou sinon il y aura une fuite de ressources dans le système d'exploitation sous-jacent, puisque le GC ne fermera jamais les descripteurs de fichiers ouverts. GC ne peut pas fermer les ressources du système d'exploitation externes à la JVM comme les descripteurs de fichiers ou les sockets. Ceux-ci doivent toujours être fermés dans une clause finally par programme ou utiliser la nouvelle syntaxe try-with-resource: docs.oracle.com/javase/tutorial/essential/exceptions
...
4
S'il vous plaît, fermez les deux flux à l'intérieur d'un enfin, s'il y a une exception, la mémoire de vos flux ne sera pas collectée.
pozuelog
121

Vous pouvez également utiliser FileChannel pour copier un fichier. Elle peut être plus rapide que la méthode de copie d'octets lors de la copie d'un fichier volumineux. Vous ne pouvez pas l'utiliser si votre fichier est supérieur à 2 Go.

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();
}
NullNoname
la source
3
transferTo peut lever une exception et dans ce cas, vous laissez les flux ouverts. Tout comme Pang et Nima ont commenté dans la réponse acceptée.
Viktor Brešan
De plus, transferTo doit être appelé à l'intérieur de la boucle car il ne garantit pas qu'il transférera le montant total demandé.
Viktor Brešan
J'ai essayé votre solution et elle échoue pour moi à l'exception java.io.FileNotFoundException: /sdcard/AppProj/IMG_20150626_214946.jpg: open failed: ENOENT (No such file or directory)de l' FileOutputStream outStream = new FileOutputStream(dst);étape. D'après le texte, je me rends compte que le fichier n'existe pas, alors je le vérifie et l'appelle dst.mkdir();si nécessaire, mais cela n'aide toujours pas. J'ai aussi essayé de vérifier dst.canWrite();et il est revenu false. Cela peut-il être la source du problème? Et oui, je l'ai <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>.
Mike B.
1
@ ViktorBrešan Après l'API 19, vous pouvez utiliser la gestion automatique des ressources Java en définissant les flux d'entrée et de sortie à l'ouverture de votre essaitry ( FileInputStream inStream = new FileInputStream(src); FileOutputStream outStream = new FileOutputStream(dst) ) {
AlbertMarkovski
Existe-t-il un moyen de faire en sorte que cette solution publie sa progression dans onProgressUpdate, afin que je puisse l'afficher dans une ProgressBar? Dans la solution acceptée, je peux calculer la progression dans la boucle while, mais je ne vois pas comment le faire ici.
George Bezerra
31

Extension Kotlin pour cela

fun File.copyTo(file: File) {
    inputStream().use { input ->
        file.outputStream().use { output ->
            input.copyTo(output)
        }
    }
}
Dima Rostopira
la source
C'est la réponse la plus concise et la plus flexible. Les réponses plus simples ne tiennent pas compte des URI ouverts via contentResolver.openInputStream(uri).
AjahnCharles
6
Kotlin est l'amour, Kotlin est la vie!
Dev Aggarwal
17

Celles-ci ont bien fonctionné pour moi

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
la source
13

C'est simple sur Android O (API 26), comme vous le voyez:

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

Il est peut-être trop tard pour une réponse, mais le moyen le plus pratique est d'utiliser

FileUtilsde

static void copyFile(File srcFile, File destFile)

par exemple c'est ce que j'ai fait

»

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
la source
20
FileUtils n'existe pas nativement sous Android.
La Berga
2
Mais il existe une bibliothèque créée par Apache qui fait cela et plus encore: commons.apache.org/proper/commons-io/javadocs/api-2.5/org/...
Alessandro Muzzi
7

Beaucoup plus simple maintenant avec Kotlin:

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

trueou falseest pour écraser le fichier de destination

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

Blundell
la source
Pas si simple si votre fichier provient d'un Uri d'intention de galerie.
AjahnCharles
Lisez les commentaires ci-dessous qui répondent: "Cette réponse est activement nuisible et ne mérite pas les votes qu'elle obtient. Elle échoue si l'URI est un contenu: // ou tout autre Uri non-fichier." (J'ai voté pour - je voulais juste préciser que ce n'est pas une
solution
5

Voici une solution qui ferme réellement les flux d'entrée / sortie si une erreur se produit lors de la copie. Cette solution utilise les méthodes Apache Commons IO IOUtils pour la copie et la gestion de la fermeture des flux.

    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
la source
Ça a l'air simple
Serg Burlaka
2

à kotlin, juste:

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

fileSrc.copyTo(fileDest)
Sodino
la source