267 votes

Comment copier les fichiers du dossier 'assets' vers la carte SD ?

J'ai quelques fichiers dans le assets dossier. Je dois les copier tous dans un dossier, disons /sdcard/folder. Je veux faire cela à partir d'un fil de discussion. Comment dois-je m'y prendre ?

0 votes

2 votes

Avant de copier/coller l'une des (excellentes !) solutions ci-dessous, pensez à utiliser cette bibliothèque pour le faire en une seule ligne de code : stackoverflow.com/a/41970539/9648

359voto

Rohith Nandakumar Points 4045

Si quelqu'un d'autre a le même problème, voici comment j'ai fait

private void copyAssets() {
    AssetManager assetManager = getAssets();
    String[] files = null;
    try {
        files = assetManager.list("");
    } catch (IOException e) {
        Log.e("tag", "Failed to get asset file list.", e);
    }
    if (files != null) for (String filename : files) {
        InputStream in = null;
        OutputStream out = null;
        try {
          in = assetManager.open(filename);
          File outFile = new File(getExternalFilesDir(null), filename);
          out = new FileOutputStream(outFile);
          copyFile(in, out);
        } catch(IOException e) {
            Log.e("tag", "Failed to copy asset file: " + filename, e);
        }     
        finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    // NOOP
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    // NOOP
                }
            }
        }  
    }
}
private void copyFile(InputStream in, OutputStream out) throws IOException {
    byte[] buffer = new byte[1024];
    int read;
    while((read = in.read(buffer)) != -1){
      out.write(buffer, 0, read);
    }
}

Référence : Déplacer un fichier en utilisant Java

28 votes

Pour écrire des fichiers dans la carte SD, il faut donner la permission dans le manifeste, par exemple : <uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE" />.

1 votes

Pourquoi ça ne marche pas pour moi. J'ai donné la permission dans le fichier manifeste. J'ai un dossier sous les actifs comme assets/myfolder/files Je fais assetManager.list("myfolder") ; il obtient les noms de fichiers comme je les imprime mais le code ne copie pas les fichiers sur la carte SD. il crée seulement le dossier à cet endroit. qu'est-ce qui ne va pas ?

22 votes

Je ne me fierais pas non plus au fait que sdcard se trouve dans /sdcard, mais je récupèrerais le chemin avec Environment.getExternalStorageDirectory().

67voto

DannyA Points 680

Sur la base de votre solution, j'ai fait quelque chose de mon cru pour autoriser les sous-dossiers. Quelqu'un pourrait trouver cela utile :

...

copyFileOrDir("myrootdir");

...

private void copyFileOrDir(String path) {
    AssetManager assetManager = this.getAssets();
    String assets[] = null;
    try {
        assets = assetManager.list(path);
        if (assets.length == 0) {
            copyFile(path);
        } else {
            String fullPath = "/data/data/" + this.getPackageName() + "/" + path;
            File dir = new File(fullPath);
            if (!dir.exists())
                dir.mkdir();
            for (int i = 0; i < assets.length; ++i) {
                copyFileOrDir(path + "/" + assets[i]);
            }
        }
    } catch (IOException ex) {
        Log.e("tag", "I/O Exception", ex);
    }
}

private void copyFile(String filename) {
    AssetManager assetManager = this.getAssets();

    InputStream in = null;
    OutputStream out = null;
    try {
        in = assetManager.open(filename);
        String newFileName = "/data/data/" + this.getPackageName() + "/" + filename;
        out = new FileOutputStream(newFileName);

        byte[] buffer = new byte[1024];
        int read;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
        in.close();
        in = null;
        out.flush();
        out.close();
        out = null;
    } catch (Exception e) {
        Log.e("tag", e.getMessage());
    }

}

1 votes

assetManager.list(path) peut être lent sur le périphérique, pour créer la liste des chemins d'accès aux actifs au préalable, ce snippet peut être utilisé à partir de assets dir : find . -name "*" -type f -exec ls -l {} \; | awk '{print substr($9,3)}' >> assets.list

3 votes

Bonne solution ! Le seul correctif nécessaire est de couper les séparateurs en tête au début de copyFileOrDir() : path= path.startsWith("/") ? path.substring(1) : path ;

0 votes

Ce débordement se produit sur certains appareils, par exemple le S5.

50voto

Yoram Cohen Points 251

La solution ci-dessus n'a pas fonctionné en raison de certaines erreurs :

  • la création du répertoire n'a pas fonctionné
  • les actifs renvoyés par Android contiennent également trois dossiers : images, sons et webkit
  • Ajout d'un moyen de traiter les fichiers volumineux : Ajoutez l'extension .mp3 au fichier dans le dossier des actifs de votre projet et pendant la copie, le fichier cible sera sans l'extension .mp3.

Voici le code (j'ai laissé les déclarations Log mais vous pouvez les laisser tomber maintenant) :

final static String TARGET_BASE_PATH = "/sdcard/appname/voices/";

private void copyFilesToSdCard() {
    copyFileOrDir(""); // copy all files in assets folder in my project
}

private void copyFileOrDir(String path) {
    AssetManager assetManager = this.getAssets();
    String assets[] = null;
    try {
        Log.i("tag", "copyFileOrDir() "+path);
        assets = assetManager.list(path);
        if (assets.length == 0) {
            copyFile(path);
        } else {
            String fullPath =  TARGET_BASE_PATH + path;
            Log.i("tag", "path="+fullPath);
            File dir = new File(fullPath);
            if (!dir.exists() && !path.startsWith("images") && !path.startsWith("sounds") && !path.startsWith("webkit"))
                if (!dir.mkdirs())
                    Log.i("tag", "could not create dir "+fullPath);
            for (int i = 0; i < assets.length; ++i) {
                String p;
                if (path.equals(""))
                    p = "";
                else 
                    p = path + "/";

                if (!path.startsWith("images") && !path.startsWith("sounds") && !path.startsWith("webkit"))
                    copyFileOrDir( p + assets[i]);
            }
        }
    } catch (IOException ex) {
        Log.e("tag", "I/O Exception", ex);
    }
}

private void copyFile(String filename) {
    AssetManager assetManager = this.getAssets();

    InputStream in = null;
    OutputStream out = null;
    String newFileName = null;
    try {
        Log.i("tag", "copyFile() "+filename);
        in = assetManager.open(filename);
        if (filename.endsWith(".jpg")) // extension was added to avoid compression on APK file
            newFileName = TARGET_BASE_PATH + filename.substring(0, filename.length()-4);
        else
            newFileName = TARGET_BASE_PATH + filename;
        out = new FileOutputStream(newFileName);

        byte[] buffer = new byte[1024];
        int read;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
        in.close();
        in = null;
        out.flush();
        out.close();
        out = null;
    } catch (Exception e) {
        Log.e("tag", "Exception in copyFile() of "+newFileName);
        Log.e("tag", "Exception in copyFile() "+e.toString());
    }

}

EDIT : Correction d'un " ;" mal placé qui provoquait une erreur systématique "could not create dir".

4 votes

Ceci doit devenir la solution !

1 votes

NOTE : Log.i("tag", "could not create dir "+fullPath) ; se produit toujours car ; est mal placé sur le if.

0 votes

C'est génial ! Merci beaucoup ! Mais pourquoi vérifiez-vous le fichier jpg ?

33voto

JPM Points 3409

Je sais que cette question a déjà été traitée mais j'ai une méthode un peu plus élégante pour copier un répertoire d'actifs vers un fichier sur la carte SD. Elle ne nécessite pas de boucle "for" mais utilise des flux de fichiers et des canaux pour faire le travail.

(Note) Si vous utilisez un type de fichier compressé, APK, PDF, ... vous pouvez renommer l'extension du fichier avant de l'insérer dans le poste et le renommer une fois que vous l'avez copié sur la carte SD.)

AssetManager am = context.getAssets();
AssetFileDescriptor afd = null;
try {
    afd = am.openFd( "MyFile.dat");

    // Create new file to copy into.
    File file = new File(Environment.getExternalStorageDirectory() + java.io.File.separator + "NewFile.dat");
    file.createNewFile();

    copyFdToFile(afd.getFileDescriptor(), file);

} catch (IOException e) {
    e.printStackTrace();
}

Un moyen de copier un fichier sans avoir à le parcourir en boucle.

public static void copyFdToFile(FileDescriptor src, File dst) throws IOException {
    FileChannel inChannel = new FileInputStream(src).getChannel();
    FileChannel outChannel = new FileOutputStream(dst).getChannel();
    try {
        inChannel.transferTo(0, inChannel.size(), outChannel);
    } finally {
        if (inChannel != null)
            inChannel.close();
        if (outChannel != null)
            outChannel.close();
    }
}

0 votes

J'ai préféré cette solution aux autres, elle est un peu plus soignée. Légère modification de la mienne qui inclut la création des fileFolders manquants. merci !

0 votes

Merci. J'ai fait cela parce que j'ai rencontré des problèmes pour boucler les fichiers s'il s'agissait de données binaires. Cela copie correctement le fichier, quel que soit son type.

0 votes

Il semblait fonctionner, mais lors de la copie des fichiers APK, il semblait se copier lui-même au lieu de l'apk. J'ai dû utiliser l'une des autres solutions à la fin.

8voto

gnac Points 598

Bon exemple. Il a répondu à ma question sur la façon d'accéder aux fichiers dans le dossier des actifs.

Le seul changement que je suggérerais est dans la boucle for. Le format suivant fonctionnerait aussi et est préféré :

    for(String filename : files) {
        InputStream in = null;
        OutputStream out = null;
        try {
          in = assetManager.open(filename);
          out = new FileOutputStream("/sdcard/" + filename);
        ...
    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X