218 votes

Trouver l'emplacement d'une carte SD amovible

Existe-t-il un moyen universel de trouver l'emplacement d'une carte SD externe ?

S'il vous plaît, ne vous confondez pas avec Stockage externe .

Environment.getExternalStorageState() renvoie le chemin du point de montage de la carte SD interne, comme "/mnt/sdcard". Mais la question concerne la carte SD externe. Comment puis-je obtenir un chemin comme "/mnt/sdcard/external_sd" (cela peut différer d'un périphérique à l'autre) ?

Je pense que je vais terminer par le filtrage de la sortie de la fonction mount par le nom du système de fichiers. Mais je ne suis pas sûr que cette méthode soit suffisamment robuste.

0 votes

Voici ma solution qui fonctionne jusqu'à Nougat : stackoverflow.com/a/40205116/5002496

0 votes

Environment.getExternalStorageState() renvoie le chemin du point de montage interne de la carte SD, comme "/mnt/sdcard". Eh bien, ce n'est pas interne dans le sens où Android utilise ce terme. Je crois que le terme que vous cherchez est "non amovible".

1 votes

adb shell echo $EXTERNAL_STORAGE

171voto

CommonsWare Points 402670

Environment.getExternalStorageState() renvoie le chemin vers le point de montage interne de la carte SD comme "/mnt/sdcard".

Non, Environment.getExternalStorageDirectory() fait référence à ce que le fabricant de l'appareil considère comme un "stockage externe". Sur certains appareils, il s'agit d'un support amovible, comme une carte SD. Sur certains appareils, il s'agit d'une partie de la mémoire flash de l'appareil. Ici, "stockage externe" signifie "le matériel accessible via le mode USB Mass Storage lorsqu'il est monté sur une machine hôte", au moins pour Android 1.x et 2.x.

Mais la question porte sur les SD externes. Comment obtenir un chemin comme "/mnt/sdcard/external_sd" (il peut être différent d'un périphérique à l'autre) ?

Android n'a pas de concept de "SD externe", en dehors du stockage externe, comme décrit ci-dessus.

Si le fabricant d'un appareil a choisi que le stockage externe soit une mémoire flash intégrée et qu'il dispose également d'une carte SD, vous devrez contacter ce fabricant pour déterminer si vous pouvez ou non utiliser la carte SD (ce qui n'est pas garanti) et quelles sont les règles pour l'utiliser, comme le chemin à utiliser.


UPDATE

Deux choses récentes à noter :

Tout d'abord, sous Android 4.4+, vous n'avez pas accès en écriture aux supports amovibles (par exemple, "SD externe"), à l'exception des emplacements sur ces supports qui pourraient être renvoyés par la fonction getExternalFilesDirs() et getExternalCacheDirs() . Voir L'excellente analyse de Dave Smith de cela, en particulier si vous voulez les détails de bas niveau.

Deuxièmement, si quelqu'un veut savoir si l'accès aux supports amovibles fait ou non partie du SDK Android, voici ce qui suit L'évaluation de Dianne Hackborn :

...gardez à l'esprit que, jusqu'à Android 4.4, la plate-forme officielle Android ne prenait pas en charge les cartes SD. tous à l'exception de deux cas particuliers : la disposition de stockage à l'ancienne où le stockage externe est une carte SD (qui est toujours prise en charge par la plate-forme aujourd'hui), et une petite fonctionnalité ajoutée à Android 3.0 qui permet de scanner les cartes SD supplémentaires, de les ajouter au fournisseur de médias et de donner aux applications un accès en lecture seule à leurs fichiers (qui est également toujours prise en charge par la plate-forme aujourd'hui).

Android 4.4 est la première version de la plate-forme qui autorise les applications à utiliser les cartes SD pour le stockage. Avant cela, tout accès à ces cartes se faisait par le biais d'API privées et non prises en charge. Nous disposons désormais d'une API assez riche dans la plate-forme qui permet aux applications d'utiliser les cartes SD d'une manière prise en charge, et d'une meilleure manière qu'auparavant : elles peuvent utiliser librement leur zone de stockage spécifique à l'application sans avoir besoin d'autorisations dans l'application, et peuvent accéder à tout autre fichier sur la carte SD à condition de passer par le sélecteur de fichiers, là encore sans avoir besoin d'autorisations spéciales.

0 votes

Merci pour l'explication.

4 votes

Et ce problème devient de plus en plus important au fur et à mesure que sortent les appareils HC et ICS qui dirigent "ExternalStorageDirectory" et tout ce qui y ressemble vers le stockage physique interne. Pour couronner le tout, la plupart des utilisateurs n'ont absolument aucune idée de la façon de localiser leur carte SD dans le système de fichiers.

307 votes

Votre réponse est donc essentiellement "contactez le fabricant". Ce qui n'est pas utile.

65voto

Richard Points 2685

J'ai trouvé la solution suivante en me basant sur certaines réponses trouvées ici.

CODE :

public class ExternalStorage {

    public static final String SD_CARD = "sdCard";
    public static final String EXTERNAL_SD_CARD = "externalSdCard";

    /**
     * @return True if the external storage is available. False otherwise.
     */
    public static boolean isAvailable() {
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
            return true;
        }
        return false;
    }

    public static String getSdCardPath() {
        return Environment.getExternalStorageDirectory().getPath() + "/";
    }

    /**
     * @return True if the external storage is writable. False otherwise.
     */
    public static boolean isWritable() {
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state)) {
            return true;
        }
        return false;

    }

    /**
     * @return A map of all storage locations available
     */
    public static Map<String, File> getAllStorageLocations() {
        Map<String, File> map = new HashMap<String, File>(10);

        List<String> mMounts = new ArrayList<String>(10);
        List<String> mVold = new ArrayList<String>(10);
        mMounts.add("/mnt/sdcard");
        mVold.add("/mnt/sdcard");

        try {
            File mountFile = new File("/proc/mounts");
            if(mountFile.exists()){
                Scanner scanner = new Scanner(mountFile);
                while (scanner.hasNext()) {
                    String line = scanner.nextLine();
                    if (line.startsWith("/dev/block/vold/")) {
                        String[] lineElements = line.split(" ");
                        String element = lineElements[1];

                        // don't add the default mount path
                        // it's already in the list.
                        if (!element.equals("/mnt/sdcard"))
                            mMounts.add(element);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            File voldFile = new File("/system/etc/vold.fstab");
            if(voldFile.exists()){
                Scanner scanner = new Scanner(voldFile);
                while (scanner.hasNext()) {
                    String line = scanner.nextLine();
                    if (line.startsWith("dev_mount")) {
                        String[] lineElements = line.split(" ");
                        String element = lineElements[2];

                        if (element.contains(":"))
                            element = element.substring(0, element.indexOf(":"));
                        if (!element.equals("/mnt/sdcard"))
                            mVold.add(element);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        for (int i = 0; i < mMounts.size(); i++) {
            String mount = mMounts.get(i);
            if (!mVold.contains(mount))
                mMounts.remove(i--);
        }
        mVold.clear();

        List<String> mountHash = new ArrayList<String>(10);

        for(String mount : mMounts){
            File root = new File(mount);
            if (root.exists() && root.isDirectory() && root.canWrite()) {
                File[] list = root.listFiles();
                String hash = "[";
                if(list!=null){
                    for(File f : list){
                        hash += f.getName().hashCode()+":"+f.length()+", ";
                    }
                }
                hash += "]";
                if(!mountHash.contains(hash)){
                    String key = SD_CARD + "_" + map.size();
                    if (map.size() == 0) {
                        key = SD_CARD;
                    } else if (map.size() == 1) {
                        key = EXTERNAL_SD_CARD;
                    }
                    mountHash.add(hash);
                    map.put(key, root);
                }
            }
        }

        mMounts.clear();

        if(map.isEmpty()){
                 map.put(SD_CARD, Environment.getExternalStorageDirectory());
        }
        return map;
    }
}

UTILISATION :

Map<String, File> externalLocations = ExternalStorage.getAllStorageLocations();
File sdCard = externalLocations.get(ExternalStorage.SD_CARD);
File externalSdCard = externalLocations.get(ExternalStorage.EXTERNAL_SD_CARD);

1 votes

Testé avec nexus 4, nexus s, galaxy s2, galaxy s3, htc desire =)

2 votes

Rebonjour, Richard - croyez-le ou non, je dois vous demander : avez-vous réellement essayé d'écrire et de lire un fichier de cette façon, et pas seulement de récupérer les répertoires ? Tu te souviens de notre vieux problème "/sdcard0" ? J'ai essayé ce code et il échoué sur un S3 lorsque j'ai essayé de relire le fichier qu'il avait écrit. ... c'est très bizarre ... et douloureux :)))

10 votes

Cela échoue sur les appareils qui n'ont pas 2 cartes SD. Il suppose que la 1ère trouvée est interne, et la 2ème trouvée est externe...

38voto

Baron Points 300

J'avais une application qui utilisait un ListPreference où l'utilisateur devait sélectionner l'endroit où il voulait sauvegarder quelque chose.

Dans cette application, j'ai scanné /proc/mounts et /system/etc/vold.fstab pour les points de montage des cartes SD. J'ai stocké les points de montage de chaque fichier dans deux fichiers distincts ArrayList s.

Ensuite, j'ai comparé une liste avec l'autre et j'ai éliminé les éléments qui n'étaient pas dans les deux listes. Cela m'a donné une liste des chemins de racine de chaque carte SD.

A partir de là, j'ai testé les chemins avec File.exists() , File.isDirectory() et File.canWrite() . Si l'un de ces tests était faux, j'éliminais ce chemin de la liste.

Ce qu'il restait dans la liste, je l'ai converti en un fichier de type String[] afin qu'il puisse être utilisé par le ListPreference l'attribut "valeurs".

Vous pouvez consulter le code ici : http://sapienmobile.com/?p=204

0 votes

Pour info, cela ne fonctionne pas sur le Galaxy S3, 2 cartes SD, une seule listée dans vold.conf.

1 votes

@3c71 - Pouvez-vous m'envoyer les fichiers vold et mounts pour le Galaxy S3 ? Je vais modifier le code pour le couvrir.

0 votes

Galaxy S, tous les chemins trouvés n'étaient pas inscriptibles, bizarre. Deux systèmes de stockage ont été trouvés, par défaut /mnt/sdcard et /storage/sdcard0, les deux n'ont pas été testés.

16voto

Vitaliy Polchuk Points 376

Comme Richard, j'utilise aussi /proc/mounts pour obtenir la liste des options de stockage disponibles

public class StorageUtils {

    private static final String TAG = "StorageUtils";

    public static class StorageInfo {

        public final String path;
        public final boolean internal;
        public final boolean readonly;
        public final int display_number;

        StorageInfo(String path, boolean internal, boolean readonly, int display_number) {
            this.path = path;
            this.internal = internal;
            this.readonly = readonly;
            this.display_number = display_number;
        }

        public String getDisplayName() {
            StringBuilder res = new StringBuilder();
            if (internal) {
                res.append("Internal SD card");
            } else if (display_number > 1) {
                res.append("SD card " + display_number);
            } else {
                res.append("SD card");
            }
            if (readonly) {
                res.append(" (Read only)");
            }
            return res.toString();
        }
    }

    public static List<StorageInfo> getStorageList() {

        List<StorageInfo> list = new ArrayList<StorageInfo>();
        String def_path = Environment.getExternalStorageDirectory().getPath();
        boolean def_path_internal = !Environment.isExternalStorageRemovable();
        String def_path_state = Environment.getExternalStorageState();
        boolean def_path_available = def_path_state.equals(Environment.MEDIA_MOUNTED)
                                    || def_path_state.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
        boolean def_path_readonly = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY);
        BufferedReader buf_reader = null;
        try {
            HashSet<String> paths = new HashSet<String>();
            buf_reader = new BufferedReader(new FileReader("/proc/mounts"));
            String line;
            int cur_display_number = 1;
            Log.d(TAG, "/proc/mounts");
            while ((line = buf_reader.readLine()) != null) {
                Log.d(TAG, line);
                if (line.contains("vfat") || line.contains("/mnt")) {
                    StringTokenizer tokens = new StringTokenizer(line, " ");
                    String unused = tokens.nextToken(); //device
                    String mount_point = tokens.nextToken(); //mount point
                    if (paths.contains(mount_point)) {
                        continue;
                    }
                    unused = tokens.nextToken(); //file system
                    List<String> flags = Arrays.asList(tokens.nextToken().split(",")); //flags
                    boolean readonly = flags.contains("ro");

                    if (mount_point.equals(def_path)) {
                        paths.add(def_path);
                        list.add(0, new StorageInfo(def_path, def_path_internal, readonly, -1));
                    } else if (line.contains("/dev/block/vold")) {
                        if (!line.contains("/mnt/secure")
                            && !line.contains("/mnt/asec")
                            && !line.contains("/mnt/obb")
                            && !line.contains("/dev/mapper")
                            && !line.contains("tmpfs")) {
                            paths.add(mount_point);
                            list.add(new StorageInfo(mount_point, false, readonly, cur_display_number++));
                        }
                    }
                }
            }

            if (!paths.contains(def_path) && def_path_available) {
                list.add(0, new StorageInfo(def_path, def_path_internal, def_path_readonly, -1));
            }

        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            if (buf_reader != null) {
                try {
                    buf_reader.close();
                } catch (IOException ex) {}
            }
        }
        return list;
    }    
}

0 votes

Merci. Cela a parfaitement fonctionné. Et j'aime la façon dont vous avez rendu StorageInfo non mutable. D'un autre côté printStackTrace ? Lorsque nous avons android.util.Log.e ?

1 votes

N'a PAS fonctionné pour les périphériques USB connectés via le câble OTG sur les Nexus 5 et Nexus 7.

1 votes

Je ne peux pas l'utiliser pour écrire un fichier sur une carte SDCard.

11voto

Jan Hudec Points 27417

Il est possible de trouver où sont montées les cartes SD supplémentaires en lisant /proc/mounts (fichier standard de Linux) et vérification croisée par rapport à vold données ( /system/etc/vold.conf ). Et notez que l'emplacement renvoyé par Environment.getExternalStorageDirectory() peut ne pas apparaître dans la configuration de vold (dans certains appareils, il s'agit d'un stockage interne qui ne peut pas être démonté), mais il doit quand même être inclus dans la liste. Cependant, nous n'avons pas trouvé de bon moyen de les décrire à l'utilisateur .

0 votes

Imo, l'utilisation de mount est plus compatible que la lecture /proc système de fichiers. Le problème est que la carte SD n'est pas nécessairement formatée en FAT. De plus, le point de montage de la carte peut varier d'une ROM à l'autre. De plus, il peut y avoir plusieurs autres partitions VFAT...

1 votes

@borisstr : Hm, en fait Android utilise vold Il est donc approprié de regarder sa configuration.

0 votes

Le fichier de code que j'ai partagé dans mon message ci-dessus comprend une méthode pour décrire à l'utilisateur les chemins de Racine découverts. Regardez le fichier setProperties() méthode.

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