Comment puis-je obtenir le chemin de la carte SD externe pour Android 4.0+?

94

Samsung Galaxy S3 dispose d'un emplacement pour carte SD externe sur lequel est monté /mnt/extSdCard.

Comment puis-je obtenir ce chemin par quelque chose comme Environment.getExternalStorageDirectory()?

Cela reviendra mnt/sdcardet je ne trouve pas l'API de la carte SD externe. (Ou stockage USB amovible sur certaines tablettes.)

Romulus Urakagi Ts'ai
la source
Pourquoi vous voulez ceci? Il est très déconseillé sur Android de faire des choses comme ça. Peut-être que si vous partagez votre motivation, nous pouvons vous orienter vers les meilleures pratiques pour ce type de chose.
rharter
2
Lorsque votre utilisateur change de téléphone, branche la carte SD sur un nouveau téléphone, et sur le premier téléphone, c'est / sdcard, sur le deuxième téléphone, c'est / mnt / extSdCard, tout ce qui utilise le chemin du fichier est bloqué. J'ai besoin de générer un chemin réel à partir de son chemin relatif.
Romulus Urakagi Ts'ai
J'ai maintenant ce sujet est vieux mais cela peut aider. vous devriez utiliser cette méthode. System.getenv (); voir le projet Environment3 pour accéder à tous les stockages connectés à votre appareil. github.com/omidfaraji/Environment3
Omid Faraji
Copie possible de Trouver un emplacement de carte SD externe
Brian Rogers
Voici ma solution qui fonctionne jusqu'à Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC

Réponses:

58

J'ai une variation sur une solution que j'ai trouvée ici

public static HashSet<String> getExternalMounts() {
    final HashSet<String> out = new HashSet<String>();
    String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
    String s = "";
    try {
        final Process process = new ProcessBuilder().command("mount")
                .redirectErrorStream(true).start();
        process.waitFor();
        final InputStream is = process.getInputStream();
        final byte[] buffer = new byte[1024];
        while (is.read(buffer) != -1) {
            s = s + new String(buffer);
        }
        is.close();
    } catch (final Exception e) {
        e.printStackTrace();
    }

    // parse output
    final String[] lines = s.split("\n");
    for (String line : lines) {
        if (!line.toLowerCase(Locale.US).contains("asec")) {
            if (line.matches(reg)) {
                String[] parts = line.split(" ");
                for (String part : parts) {
                    if (part.startsWith("/"))
                        if (!part.toLowerCase(Locale.US).contains("vold"))
                            out.add(part);
                }
            }
        }
    }
    return out;
}

La méthode originale a été testée et travaillée avec

  • Huawei X3 (en stock)
  • Galaxy S2 (en stock)
  • Galaxy S3 (en stock)

Je ne suis pas sûr de la version d'Android sur laquelle ils étaient lorsqu'ils ont été testés.

J'ai testé ma version modifiée avec

  • Moto Xoom 4.1.2 (en stock)
  • Galaxy Nexus (cyanogenmod 10) à l'aide d'un câble OTG
  • HTC Incredible (cyanogenmod 7.2) cela a renvoyé à la fois l'interne et l'externe. Ce périphérique est un peu étrange en ce sens que son interne reste largement inutilisé car getExternalStorage () renvoie un chemin vers la carte SD à la place.

et certains périphériques de stockage uniques qui utilisent une carte SD comme stockage principal

  • HTC G1 (cyanogenmod 6.1)
  • HTC G1 (en stock)
  • HTC Vision / G2 (en stock)

À l'exception de l'incroyable, tous ces appareils n'ont rendu que leur stockage amovible. Il y a probablement des vérifications supplémentaires que je devrais faire, mais c'est au moins un peu mieux que n'importe quelle solution que j'ai trouvée jusqu'à présent.

Gnathonique
la source
3
Je pense que votre solution donnera un nom de dossier invalide si l'original contient des lettres majuscules comme / mnt / SdCard
Eugene Popovich
1
J'ai testé sur certains appareils Motorola, Samsung et LG et cela a parfaitement fonctionné.
pepedeutico
2
@KhawarRaza, cette méthode a cessé de fonctionner à partir de kitkat. Utilisez la méthode de Dmitriy Lozenko, avec ceci comme solution de secours si vous prenez en charge des périphériques pré-ics.
Gnathonic
1
Fonctionne sur HUAWEI Honor 3c. Merci.
li2
2
ce retour est / mnt au lieu de / stockage sur 4.0+ pour moi sur un Galaxy Note 3
ono
54

J'ai trouvé un moyen plus fiable d'obtenir des chemins vers toutes les cartes SD du système. Cela fonctionne sur toutes les versions d'Android et renvoie les chemins vers tous les stockages (y compris émulés).

Fonctionne correctement sur tous mes appareils.

PS: basé sur le code source de la classe Environnement.

private static final Pattern DIR_SEPORATOR = Pattern.compile("/");

/**
 * Raturns all available SD-Cards in the system (include emulated)
 *
 * Warning: Hack! Based on Android source code of version 4.3 (API 18)
 * Because there is no standart way to get it.
 * TODO: Test on future Android versions 4.4+
 *
 * @return paths to all available SD-Cards in the system (include emulated)
 */
public static String[] getStorageDirectories()
{
    // Final set of paths
    final Set<String> rv = new HashSet<String>();
    // Primary physical SD-CARD (not emulated)
    final String rawExternalStorage = System.getenv("EXTERNAL_STORAGE");
    // All Secondary SD-CARDs (all exclude primary) separated by ":"
    final String rawSecondaryStoragesStr = System.getenv("SECONDARY_STORAGE");
    // Primary emulated SD-CARD
    final String rawEmulatedStorageTarget = System.getenv("EMULATED_STORAGE_TARGET");
    if(TextUtils.isEmpty(rawEmulatedStorageTarget))
    {
        // Device has physical external storage; use plain paths.
        if(TextUtils.isEmpty(rawExternalStorage))
        {
            // EXTERNAL_STORAGE undefined; falling back to default.
            rv.add("/storage/sdcard0");
        }
        else
        {
            rv.add(rawExternalStorage);
        }
    }
    else
    {
        // Device has emulated storage; external storage paths should have
        // userId burned into them.
        final String rawUserId;
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)
        {
            rawUserId = "";
        }
        else
        {
            final String path = Environment.getExternalStorageDirectory().getAbsolutePath();
            final String[] folders = DIR_SEPORATOR.split(path);
            final String lastFolder = folders[folders.length - 1];
            boolean isDigit = false;
            try
            {
                Integer.valueOf(lastFolder);
                isDigit = true;
            }
            catch(NumberFormatException ignored)
            {
            }
            rawUserId = isDigit ? lastFolder : "";
        }
        // /storage/emulated/0[1,2,...]
        if(TextUtils.isEmpty(rawUserId))
        {
            rv.add(rawEmulatedStorageTarget);
        }
        else
        {
            rv.add(rawEmulatedStorageTarget + File.separator + rawUserId);
        }
    }
    // Add all secondary storages
    if(!TextUtils.isEmpty(rawSecondaryStoragesStr))
    {
        // All Secondary SD-CARDs splited into array
        final String[] rawSecondaryStorages = rawSecondaryStoragesStr.split(File.pathSeparator);
        Collections.addAll(rv, rawSecondaryStorages);
    }
    return rv.toArray(new String[rv.size()]);
}
Dmitriy Lozenko
la source
5
Que devait être DIR_SEPORATOR?
cYrixmorten
1
@cYrixmorten c'est fourni en plus du code .. c'est un Patternpour/
Ankit Bansal
Quelqu'un a testé cette solution sur Lolipop?
lenrok258
J'ai essayé cette méthode sur lenovo P780, elle ne fonctionne pas.
Satheesh Kumar
2
Fonctionne jusqu'à Lollipop; ne fonctionne pas sur Marshmallow (Galaxy Tab A w / 6.0.1) où System.getenv("SECONDARY_STORAGE")revient /storage/sdcard1. Cet emplacement n'existe pas, mais l'emplacement réel est /storage/42CE-FD0A, où 42CE-FD0A est le numéro de série du volume de la partition formatée.
DaveAlden
32

Je suppose que pour utiliser la carte SD externe, vous devez utiliser ceci:

new File("/mnt/external_sd/")

OU

new File("/mnt/extSdCard/")

dans ton cas...

en remplacement de Environment.getExternalStorageDirectory()

Travaille pour moi. Vous devriez d'abord vérifier ce qui se trouve dans le répertoire mnt et travailler à partir de là.


Vous devez utiliser un type de méthode de sélection pour choisir la carte SD à utiliser:

File storageDir = new File("/mnt/");
if(storageDir.isDirectory()){
    String[] dirList = storageDir.list();
    //TODO some type of selecton method?
}
FabianCook
la source
1
En fait, je veux une méthode plutôt qu'un chemin de code en dur, mais la liste / mnt / devrait convenir. Je vous remercie.
Romulus Urakagi Ts'ai
Aucun problème. Ayez une option dans les paramètres à partir de laquelle vous choisissez le chemin, puis utilisez la méthode pour le récupérer.
FabianCook
10
Malheureusement, la carte SD externe peut se trouver dans un dossier différent de / mnt. Par exemple, sur Samsung GT-I9305 c'est / storage / extSdCard tandis que la carte SD intégrée a le chemin / storage / sdcard0
iseeall
2
Eh bien, vous obtiendrez l'emplacement de la carte SD, puis son répertoire parent.
FabianCook
Quel sera le chemin de la carte SD externe (pour la clé USB connectée à la tablette via un câble OTG) pour Android 4.0+?
osimer pothe
15

Afin de récupérer tous les stockages externes (qu'il s'agisse de cartes SD ou de stockages internes non amovibles ), vous pouvez utiliser le code suivant:

final String state = Environment.getExternalStorageState();

if ( Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state) ) {  // we can read the External Storage...           
    //Retrieve the primary External Storage:
    final File primaryExternalStorage = Environment.getExternalStorageDirectory();

    //Retrieve the External Storages root directory:
    final String externalStorageRootDir;
    if ( (externalStorageRootDir = primaryExternalStorage.getParent()) == null ) {  // no parent...
        Log.d(TAG, "External Storage: " + primaryExternalStorage + "\n");
    }
    else {
        final File externalStorageRoot = new File( externalStorageRootDir );
        final File[] files = externalStorageRoot.listFiles();

        for ( final File file : files ) {
            if ( file.isDirectory() && file.canRead() && (file.listFiles().length > 0) ) {  // it is a real directory (not a USB drive)...
                Log.d(TAG, "External Storage: " + file.getAbsolutePath() + "\n");
            }
        }
    }
}

Vous pouvez également utiliser System.getenv ("EXTERNAL_STORAGE") pour récupérer le répertoire de stockage externe principal (par exemple "/ storage / sdcard0" ) et System.getenv ("SECONDARY_STORAGE") pour récupérer la liste de tous les répertoires secondaires (par exemple " / stockage / extSdCard: / stockage / UsbDriveA: / stockage / UsbDriveB " ). N'oubliez pas que, dans ce cas également, vous souhaiterez peut-être filtrer la liste des répertoires secondaires afin d'exclure les clés USB.

Dans tous les cas, veuillez noter que l'utilisation de chemins codés en dur est toujours une mauvaise approche (surtout lorsque chaque fabricant peut le modifier à sa guise).

Paolo Rovelli
la source
System.getenv ("SECONDARY_STORAGE") n'a PAS fonctionné pour les périphériques USB connectés via un câble OTG sur les Nexus 5 et Nexus 7.
Khawar Raza
Voici ma solution qui fonctionne jusqu'à Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC
14

J'utilisais Dmitriy Lozenko la solution « jusqu'à ce que je vérifié sur un Asus Zenfone2 , Marshmallow 6.0.1 et la solution ne fonctionne pas. La solution a échoué lors de l'obtention de EMULATED_STORAGE_TARGET , spécifiquement pour le chemin microSD, c'est-à-dire: / storage / F99C-10F4 / . J'ai modifié le code pour obtenir les chemins racine émulés directement à partir des chemins d'application émulés context.getExternalFilesDirs(null);et ajouter des chemins physiques spécifiques au modèle de téléphone plus connus .

Pour nous faciliter la vie, j'ai fait une bibliothèque ici . Vous pouvez l'utiliser via le système de construction gradle, maven, sbt et leiningen.

Si vous aimez la méthode à l'ancienne, vous pouvez également copier-coller le fichier directement à partir d' ici , mais vous ne saurez pas s'il y a une mise à jour dans le futur sans le vérifier manuellement.

Si vous avez des questions ou des suggestions, veuillez me le faire savoir

HendraWD
la source
1
Je ne l'ai pas beaucoup utilisé, mais cela fonctionne bien pour mon problème.
David
1
Excellente solution! Une note cependant - si la carte SD est retirée lorsque l'application est active, context.getExternalFilesDirs(null)retournera nullpour l'un des fichiers et il y aura une exception dans la boucle suivante. Je suggère d'ajouter if (file == null) continue;comme première ligne de la boucle.
Koger
1
@Koger merci pour la suggestion, je l'ai ajoutée et corrigé une faute de frappe dans ma réponse
HendraWD
13

Bonnes nouvelles! Dans KitKat, il existe désormais une API publique pour interagir avec ces périphériques de stockage partagés secondaires.

Les nouvelles méthodes Context.getExternalFilesDirs()et Context.getExternalCacheDirs()peuvent renvoyer plusieurs chemins, y compris les périphériques principaux et secondaires. Vous pouvez ensuite les parcourir et vérifier Environment.getStorageState()et File.getFreeSpace()déterminer le meilleur endroit pour stocker vos fichiers. Ces méthodes sont également disponibles ContextCompatdans la bibliothèque support-v4.

Notez également que si vous ne souhaitez utiliser que les répertoires renvoyés par Context, vous n'avez plus besoin des autorisations READ_ou WRITE_EXTERNAL_STORAGE. À l'avenir, vous aurez toujours un accès en lecture / écriture à ces répertoires sans aucune autorisation supplémentaire requise.

Les applications peuvent également continuer à travailler sur des appareils plus anciens en mettant fin à leur demande d'autorisation comme ceci:

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="18" />
Jeff Sharkey
la source
Je vous remercie. J'ai créé un code qui suit ce que vous avez écrit et je pense que cela fonctionne bien pour trouver tous les chemins de cartes SD. Pouvez-vous jeter un oeil? Ici: stackoverflow.com/a/27197248/878126. BTW, vous ne devriez pas utiliser "getFreeSpace" car théoriquement, l'espace libre peut changer pendant l'exécution.
développeur android
10

J'ai fait ce qui suit pour accéder à toutes les cartes SD externes.

Avec:

File primaryExtSd=Environment.getExternalStorageDirectory();

vous obtenez le chemin vers la SD externe principale Puis avec:

File parentDir=new File(primaryExtSd.getParent());

vous obtenez le répertoire parent du stockage externe principal, et il est également le parent de tous les sd externes. Maintenant, vous pouvez répertorier tout le stockage et sélectionner celui que vous souhaitez.

J'espère que c'est utile.

valenta
la source
Cela devrait être la réponse acceptée, et merci, c'est utile.
Meanman
9

Voici comment obtenir la liste des chemins de la carte SD (à l'exclusion du stockage externe principal):

  /**
   * returns a list of all available sd cards paths, or null if not found.
   * 
   * @param includePrimaryExternalStorage set to true if you wish to also include the path of the primary external storage
   */
  @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  public static List<String> getSdCardPaths(final Context context,final boolean includePrimaryExternalStorage)
    {
    final File[] externalCacheDirs=ContextCompat.getExternalCacheDirs(context);
    if(externalCacheDirs==null||externalCacheDirs.length==0)
      return null;
    if(externalCacheDirs.length==1)
      {
      if(externalCacheDirs[0]==null)
        return null;
      final String storageState=EnvironmentCompat.getStorageState(externalCacheDirs[0]);
      if(!Environment.MEDIA_MOUNTED.equals(storageState))
        return null;
      if(!includePrimaryExternalStorage&&VERSION.SDK_INT>=VERSION_CODES.HONEYCOMB&&Environment.isExternalStorageEmulated())
        return null;
      }
    final List<String> result=new ArrayList<>();
    if(includePrimaryExternalStorage||externalCacheDirs.length==1)
      result.add(getRootOfInnerSdCardFolder(externalCacheDirs[0]));
    for(int i=1;i<externalCacheDirs.length;++i)
      {
      final File file=externalCacheDirs[i];
      if(file==null)
        continue;
      final String storageState=EnvironmentCompat.getStorageState(file);
      if(Environment.MEDIA_MOUNTED.equals(storageState))
        result.add(getRootOfInnerSdCardFolder(externalCacheDirs[i]));
      }
    if(result.isEmpty())
      return null;
    return result;
    }

  /** Given any file/folder inside an sd card, this will return the path of the sd card */
  private static String getRootOfInnerSdCardFolder(File file)
    {
    if(file==null)
      return null;
    final long totalSpace=file.getTotalSpace();
    while(true)
      {
      final File parentFile=file.getParentFile();
      if(parentFile==null||parentFile.getTotalSpace()!=totalSpace||!parentFile.canRead())
        return file.getAbsolutePath();
      file=parentFile;
      }
    }
développeur android
la source
externalCacheDirs[0].getParentFile().getParentFile().getParentFile().getParentFile().getAbsolutePath()Ceci est une recette pour le désastre. Si vous faites cela, vous pouvez tout aussi bien coder en dur le chemin que vous voulez, car si vous obtenez un répertoire de cache avec autre chose que 4 parents, vous obtiendrez NullPointerException ou le mauvais chemin.
rharter
@rharter Correct. Correction de ce problème également. Jetez un coup d'oeil s'il vous plait.
développeur android
La documentation ( developer.android.com/reference/android/content/… ) dit: "Les périphériques de stockage externes renvoyés ici sont considérés comme une partie permanente de l'appareil, y compris à la fois le stockage externe émulé et les emplacements de supports physiques, tels que les cartes SD dans une batterie compartiment. Les chemins renvoyés n'incluent pas les périphériques temporaires, tels que les clés USB. " Il semble qu'ils n'incluent pas non plus les emplacements pour carte SD, c'est-à-dire les endroits où vous pouvez retirer une carte SD sans passer sous la batterie. Difficile à dire, cependant.
LarsH
1
@LarsH Eh bien, je l'ai testé moi-même sur SGS3 (et une vraie carte SD), donc je pense que cela devrait également fonctionner sur d'autres appareils.
développeur android
5

Merci pour les indices fournis par vous, en particulier @SmartLemon, j'ai la solution. Au cas où quelqu'un d'autre en aurait besoin, je mets ma solution finale ici (pour trouver la première carte SD externe répertoriée):

public File getExternalSDCardDirectory()
{
    File innerDir = Environment.getExternalStorageDirectory();
    File rootDir = innerDir.getParentFile();
    File firstExtSdCard = innerDir ;
    File[] files = rootDir.listFiles();
    for (File file : files) {
        if (file.compareTo(innerDir) != 0) {
            firstExtSdCard = file;
            break;
        }
    }
    //Log.i("2", firstExtSdCard.getAbsolutePath().toString());
    return firstExtSdCard;
}

S'il n'y a pas de carte SD externe, alors il renvoie le stockage à bord. Je vais l'utiliser si la carte SD n'existe pas, vous devrez peut-être la changer.

rml
la source
1
ce jetant null sur Android version 19. rootDir.listFiles () retourne null. Je l'ai testé avec Nexus 7, émulateur et Galaxy Note.
Manu Zi
3

reportez-vous à mon code, j'espère que cela vous sera utile:

    Runtime runtime = Runtime.getRuntime();
    Process proc = runtime.exec("mount");
    InputStream is = proc.getInputStream();
    InputStreamReader isr = new InputStreamReader(is);
    String line;
    String mount = new String();
    BufferedReader br = new BufferedReader(isr);
    while ((line = br.readLine()) != null) {
        if (line.contains("secure")) continue;
        if (line.contains("asec")) continue;

        if (line.contains("fat")) {//TF card
            String columns[] = line.split(" ");
            if (columns != null && columns.length > 1) {
                mount = mount.concat("*" + columns[1] + "\n");
            }
        } else if (line.contains("fuse")) {//internal storage
            String columns[] = line.split(" ");
            if (columns != null && columns.length > 1) {
                mount = mount.concat(columns[1] + "\n");
            }
        }
    }
    txtView.setText(mount);
cst05001
la source
2

En fait, sur certains périphériques, le nom par défaut de la carte SD externe s'affiche comme extSdCardet pour d'autres sdcard1.

Cet extrait de code aide à trouver ce chemin exact et vous aide à récupérer le chemin du périphérique externe.

String sdpath,sd1path,usbdiskpath,sd0path;    
        if(new File("/storage/extSdCard/").exists())
            {
               sdpath="/storage/extSdCard/";
               Log.i("Sd Cardext Path",sdpath);
            }
        if(new File("/storage/sdcard1/").exists())
         {
              sd1path="/storage/sdcard1/";
              Log.i("Sd Card1 Path",sd1path);
         }
        if(new File("/storage/usbcard1/").exists())
         {
              usbdiskpath="/storage/usbcard1/";
              Log.i("USB Path",usbdiskpath);
         }
        if(new File("/storage/sdcard0/").exists())
         {
              sd0path="/storage/sdcard0/";
              Log.i("Sd Card0 Path",sd0path);
         }
Sam
la source
Cela m'a énormément aidé.
Droid Chris
Certains appareils utilisent "/ storage / external_sd" (LG G3 KitKat), Marshmallow a un schéma différent, le stockage interne du Nexus 5X utilisant Android 6.0 est "/ mnt / sdcard" et la carte SD externe se trouve sous "/ storage / XXXX-XXXX "
AB
2

Oui. Différents fabricants utilisent un nom de carte SD différent comme dans Samsung Tab 3 son extsd, et d'autres appareils Samsung utilisent une carte SD comme ce fabricant différent utilise des noms différents.

J'avais la même exigence que vous. J'ai donc créé un exemple d'exemple pour vous à partir de mon projet. Aller à ce lien Exemple de sélecteur d'annuaire Android qui utilise la bibliothèque androi-dirchooser. Cet exemple détecte la carte SD et répertorie tous les sous-dossiers et détecte également si l'appareil a plus d'une carte SD.

Une partie du code ressemble à ceci Pour un exemple complet, allez au lien Sélecteur d'annuaire Android

/**
* Returns the path to internal storage ex:- /storage/emulated/0
 *
* @return
 */
private String getInternalDirectoryPath() {
return Environment.getExternalStorageDirectory().getAbsolutePath();
 }

/**
 * Returns the SDcard storage path for samsung ex:- /storage/extSdCard
 *
 * @return
 */
    private String getSDcardDirectoryPath() {
    return System.getenv("SECONDARY_STORAGE");
}


 mSdcardLayout.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View view) {
        String sdCardPath;
        /***
         * Null check because user may click on already selected buton before selecting the folder
         * And mSelectedDir may contain some wrong path like when user confirm dialog and swith back again
         */

        if (mSelectedDir != null && !mSelectedDir.getAbsolutePath().contains(System.getenv("SECONDARY_STORAGE"))) {
            mCurrentInternalPath = mSelectedDir.getAbsolutePath();
        } else {
            mCurrentInternalPath = getInternalDirectoryPath();
        }
        if (mCurrentSDcardPath != null) {
            sdCardPath = mCurrentSDcardPath;
        } else {
            sdCardPath = getSDcardDirectoryPath();
        }
        //When there is only one SDcard
        if (sdCardPath != null) {
            if (!sdCardPath.contains(":")) {
                updateButtonColor(STORAGE_EXTERNAL);
                File dir = new File(sdCardPath);
                changeDirectory(dir);
            } else if (sdCardPath.contains(":")) {
                //Multiple Sdcards show root folder and remove the Internal storage from that.
                updateButtonColor(STORAGE_EXTERNAL);
                File dir = new File("/storage");
                changeDirectory(dir);
            }
        } else {
            //In some unknown scenario at least we can list the root folder
            updateButtonColor(STORAGE_EXTERNAL);
            File dir = new File("/storage");
            changeDirectory(dir);
        }


    }
});
Shivaraj Patil
la source
2

Cette solution (assemblée à partir d'autres réponses à cette question) gère le fait (comme mentionné par @ono) qui System.getenv("SECONDARY_STORAGE")n'est d'aucune utilité avec Marshmallow.

Testé et travaille sur:

  • Samsung Galaxy Tab 2 (Android 4.1.1 - Stock)
  • Samsung Galaxy Note 8.0 (Android 4.2.2 - Stock)
  • Samsung Galaxy S4 (Android 4.4 - Stock)
  • Samsung Galaxy S4 (Android 5.1.1 - Cyanogenmod)
  • Samsung Galaxy Tab A (Android 6.0.1 - Stock)

    /**
     * Returns all available external SD-Card roots in the system.
     *
     * @return paths to all available external SD-Card roots in the system.
     */
    public static String[] getStorageDirectories() {
        String [] storageDirectories;
        String rawSecondaryStoragesStr = System.getenv("SECONDARY_STORAGE");
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            List<String> results = new ArrayList<String>();
            File[] externalDirs = applicationContext.getExternalFilesDirs(null);
            for (File file : externalDirs) {
                String path = file.getPath().split("/Android")[0];
                if((Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Environment.isExternalStorageRemovable(file))
                        || rawSecondaryStoragesStr != null && rawSecondaryStoragesStr.contains(path)){
                    results.add(path);
                }
            }
            storageDirectories = results.toArray(new String[0]);
        }else{
            final Set<String> rv = new HashSet<String>();
    
            if (!TextUtils.isEmpty(rawSecondaryStoragesStr)) {
                final String[] rawSecondaryStorages = rawSecondaryStoragesStr.split(File.pathSeparator);
                Collections.addAll(rv, rawSecondaryStorages);
            }
            storageDirectories = rv.toArray(new String[rv.size()]);
        }
        return storageDirectories;
    }
DaveAlden
la source
La condition rawSecondaryStoragesStr.contains(path)peut également permettre au stockage interne d'être ajouté; nous pouvons mieux supprimer cette condition .. J'ai rencontré ce problème, car mon appareil renvoie le répertoire de stockage interne avec System.getenv("SECONDARY_STORAGE"), mais renvoie le chemin de la carte mémoire externe avec System.getenv(EXTERNAL_STORAGE)sur KitKat; il semble que différents fournisseurs aient mis en œuvre cela différemment sans aucune norme.
Gokul NC
Voici ma solution qui fonctionne jusqu'à Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC
1

Sur certains appareils (par exemple samsung galaxy sII), la carte mémoire interne peut être en vfat. Dans ce cas, utilisez le dernier code, nous obtenons le chemin de la carte mémoire interne (/ mnt / sdcad) mais pas de carte externe. Le code ci-dessous résout ce problème.

static String getExternalStorage(){
         String exts =  Environment.getExternalStorageDirectory().getPath();
         try {
            FileReader fr = new FileReader(new File("/proc/mounts"));       
            BufferedReader br = new BufferedReader(fr);
            String sdCard=null;
            String line;
            while((line = br.readLine())!=null){
                if(line.contains("secure") || line.contains("asec")) continue;
            if(line.contains("fat")){
                String[] pars = line.split("\\s");
                if(pars.length<2) continue;
                if(pars[1].equals(exts)) continue;
                sdCard =pars[1]; 
                break;
            }
        }
        fr.close();
        br.close();
        return sdCard;  

     } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return null;
}
Alex
la source
1
       File[] files = null;
    File file = new File("/storage");// /storage/emulated
if (file.exists()) {
        files = file.listFiles();
            }
            if (null != files)
                for (int j = 0; j < files.length; j++) {
                    Log.e(TAG, "" + files[j]);
                    Log.e(TAG, "//--//--// " +             files[j].exists());

                    if (files[j].toString().replaceAll("_", "")
                            .toLowerCase().contains("extsdcard")) {
                        external_path = files[j].toString();
                        break;
                    } else if (files[j].toString().replaceAll("_", "")
                            .toLowerCase()
                            .contains("sdcard".concat(Integer.toString(j)))) {
                        // external_path = files[j].toString();
                    }
                    Log.e(TAG, "--///--///--  " + external_path);
                }
Amit
la source
0

Je suis sûr que ce code résoudra sûrement vos problèmes ... Cela fonctionne bien pour moi ... \

try {
            File mountFile = new File("/proc/mounts");
            usbFoundCount=0;
            sdcardFoundCount=0;
            if(mountFile.exists())
             {
                Scanner usbscanner = new Scanner(mountFile);
                while (usbscanner.hasNext()) {
                    String line = usbscanner.nextLine();
                    if (line.startsWith("/dev/fuse /storage/usbcard1")) {
                        usbFoundCount=1;
                        Log.i("-----USB--------","USB Connected and properly mounted---/dev/fuse /storage/usbcard1" );
                    }
            }
         }
            if(mountFile.exists()){
                Scanner sdcardscanner = new Scanner(mountFile);
                while (sdcardscanner.hasNext()) {
                    String line = sdcardscanner.nextLine();
                    if (line.startsWith("/dev/fuse /storage/sdcard1")) {
                        sdcardFoundCount=1;
                        Log.i("-----USB--------","USB Connected and properly mounted---/dev/fuse /storage/sdcard1" );
                    }
            }
         }
            if(usbFoundCount==1)
            {
                Toast.makeText(context,"USB Connected and properly mounted", 7000).show();
                Log.i("-----USB--------","USB Connected and properly mounted" );
            }
            else
            {
                Toast.makeText(context,"USB not found!!!!", 7000).show();
                Log.i("-----USB--------","USB not found!!!!" );

            }
            if(sdcardFoundCount==1)
            {
                Toast.makeText(context,"SDCard Connected and properly mounted", 7000).show();
                Log.i("-----SDCard--------","SDCard Connected and properly mounted" );
            }
            else
            {
                Toast.makeText(context,"SDCard not found!!!!", 7000).show();
                Log.i("-----SDCard--------","SDCard not found!!!!" );

            }
        }catch (Exception e) {
            e.printStackTrace();
        } 
Sam
la source
Environment.getExternalStorageDirectory () ne doit pas vous donner l'emplacement exact de votre sdcard externe ou de l'emplacement USB. Il suffit d'analyser l'emplacement "/ proc / mounts" puis / dev / fuse / storage / sdcard1 "vous donnera l'emplacement si votre sdcard est correctement monté ou pas le même pour l'usb aussi. De cette façon, vous pouvez facilement l'obtenir..Cheers..Sam
Sam
0

ce n'est pas vrai. / mnt / sdcard / external_sd peut exister même si la carte SD n'est pas montée. votre application plante lorsque vous essayez d'écrire dans / mnt / sdcard / external_sd lorsqu'elle n'est pas montée.

vous devez vérifier si la carte SD est montée en premier en utilisant:

boolean isSDPresent = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
Marcin Skoczylas
la source
7
Cela n'a pas de sens lorsque getExternalStorageState()renvoie le stockage interne plutôt que la carte SD externe, je pense.
Romulus Urakagi Ts'ai
Fournissent-ils une fonction pour obtenir une carte SD externe maintenant? Je ne trouve aucun indice après avoir cherché pendant une heure. Si tel est le cas, c'est tellement étrange après tant de versions mises à jour.
rml
Quand vous avez dit "ce n'est pas vrai", à quoi faisiez-vous référence? Je sais, c'était il y a 2,5 ans ...
LarsH
0
 String path = Environment.getExternalStorageDirectory()
                        + File.separator + Environment.DIRECTORY_PICTURES;
                File dir = new File(path);
sarra srairi
la source
4
Ça ne marche pas. Il renvoie le chemin du stockage interne. ie / storage /
0

Vous pouvez utiliser quelque chose comme - Context.getExternalCacheDirs () ou Context.getExternalFilesDirs () ou Context.getObbDirs (). Ils fournissent des répertoires spécifiques à l'application dans tous les périphériques de stockage externes où l'application peut stocker ses fichiers.

Donc quelque chose comme ça - Context.getExternalCacheDirs () [i] .getParentFile (). GetParentFile (). GetParentFile (). GetParent () peut vous trouver le chemin racine des périphériques de stockage externes.

Je sais que ces commandes ont un but différent, mais d'autres réponses n'ont pas fonctionné pour moi.

Ce lien m'a donné de bons conseils - https://possiblemobile.com/2014/03/android-external-storage/

Sharath Holla
la source
0

System.getenv("SECONDARY_STORAGE")renvoie null pour Marshmallow. C'est une autre façon de trouver tous les répertoires externes. Vous pouvez vérifier s'il est amovible, ce qui détermine si interne / externe

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    File[] externalCacheDirs = context.getExternalCacheDirs();
    for (File file : externalCacheDirs) {
        if (Environment.isExternalStorageRemovable(file)) {
            // It's a removable storage
        }
    }
}
ono
la source
0

J'ai essayé les solutions fournies par Dmitriy Lozenko et Gnathonic sur mon Samsung Galaxy Tab S2 (Modèle: T819Y) mais aucune ne m'a aidé à récupérer le chemin vers un répertoire de carte SD externe. mountl'exécution de la commande contenait le chemin requis vers le répertoire de la carte SD externe (c'est-à-dire / Storage / A5F9-15F4) mais il ne correspondait pas à l'expression régulière et n'a donc pas été renvoyé. Je ne reçois pas le mécanisme de dénomination de répertoire suivi par Samsung. Pourquoi ils s'écartent des normes (extsdcard) et proposent quelque chose de vraiment louche comme dans mon cas (par exemple / Storage / A5F9-15F4) . Y a-t-il quelque chose qui me manque? Quoi qu'il en soit, suite aux changements dans l'expression régulière de Gnathonic La solution m'a aidé à obtenir un répertoire sdcard valide:

final HashSet<String> out = new HashSet<String>();
        String reg = "(?i).*(vold|media_rw).*(sdcard|vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
        String s = "";
        try {
            final Process process = new ProcessBuilder().command("mount")
                    .redirectErrorStream(true).start();
            process.waitFor();
            final InputStream is = process.getInputStream();
            final byte[] buffer = new byte[1024];
            while (is.read(buffer) != -1) {
                s = s + new String(buffer);
            }
            is.close();
        } catch (final Exception e) {
            e.printStackTrace();
        }

        // parse output
        final String[] lines = s.split("\n");
        for (String line : lines) {
            if (!line.toLowerCase(Locale.US).contains("asec")) {
                if (line.matches(reg)) {
                    String[] parts = line.split(" ");
                    for (String part : parts) {
                        if (part.startsWith("/"))
                            if (!part.toLowerCase(Locale.US).contains("vold"))
                                out.add(part);
                    }
                }
            }
        }
        return out;

Je ne sais pas si c'est une solution valide et si elle donnera des résultats pour d'autres tablettes Samsung, mais cela a résolu mon problème pour le moment. Voici une autre méthode pour récupérer le chemin de la carte SD amovible dans Android (v6.0). J'ai testé la méthode avec Android guimauve et cela fonctionne. L'approche utilisée est très basique et fonctionnera sûrement pour d'autres versions aussi, mais les tests sont obligatoires. Un aperçu de cela sera utile:

public static String getSDCardDirPathForAndroidMarshmallow() {

    File rootDir = null;

    try {
        // Getting external storage directory file
        File innerDir = Environment.getExternalStorageDirectory();

        // Temporarily saving retrieved external storage directory as root
        // directory
        rootDir = innerDir;

        // Splitting path for external storage directory to get its root
        // directory

        String externalStorageDirPath = innerDir.getAbsolutePath();

        if (externalStorageDirPath != null
                && externalStorageDirPath.length() > 1
                && externalStorageDirPath.startsWith("/")) {

            externalStorageDirPath = externalStorageDirPath.substring(1,
                    externalStorageDirPath.length());
        }

        if (externalStorageDirPath != null
                && externalStorageDirPath.endsWith("/")) {

            externalStorageDirPath = externalStorageDirPath.substring(0,
                    externalStorageDirPath.length() - 1);
        }

        String[] pathElements = externalStorageDirPath.split("/");

        for (int i = 0; i < pathElements.length - 1; i++) {

            rootDir = rootDir.getParentFile();
        }

        File[] files = rootDir.listFiles();

        for (File file : files) {
            if (file.exists() && file.compareTo(innerDir) != 0) {

                // Try-catch is implemented to prevent from any IO exception
                try {

                    if (Environment.isExternalStorageRemovable(file)) {
                        return file.getAbsolutePath();

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

            }
        }
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

Veuillez partager si vous avez une autre approche pour gérer ce problème. Merci

Abdul Rehman
la source
Voici ma solution qui fonctionne jusqu'à Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC
J'ai en fait le même problème avec vous sur Asus Zenfone 2, mais je change le code de Dmitriy Lozenko à la place pour résoudre le problème stackoverflow.com/a/40582634/3940133
HendraWD
@HendraWD, la solution que vous suggérez peut ne pas fonctionner avec Android Marshmallow (6.0) car la prise en charge des variables d'environnement y est supprimée. Je ne me souviens pas exactement mais je pense avoir essayé.
Abdul Rehman
@GokulNC Je vais essayer. semble légitime :)
Abdul Rehman
@GokulNC oui, c'est pourquoi j'utilise context.getExternalFilesDirs (null); au lieu d'utiliser directement les chemins physiques lorsque la version est> Marshmallow
HendraWD
0
String secStore = System.getenv("SECONDARY_STORAGE");

File externalsdpath = new File(secStore);

Cela obtiendra le chemin du stockage secondaire externe sd.

Kenneth Villamin
la source
0
//manifest file outside the application tag
//please give permission write this 
//<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
        File file = new File("/mnt");
        String[] fileNameList = file.list(); //file names list inside the mnr folder
        String all_names = ""; //for the log information
        String foundedFullNameOfExtCard = ""; // full name of ext card will come here
        boolean isExtCardFounded = false;
        for (String name : fileNameList) {
            if (!isExtCardFounded) {
                isExtCardFounded = name.contains("ext");
                foundedFullNameOfExtCard = name;
            }
            all_names += name + "\n"; // for log
        }
        Log.d("dialog", all_names + foundedFullNameOfExtCard);
Emre Kilinc Arslan
la source
Ceci est alternativement pour les débutants au moins pour obtenir une stringList de tous les pilotes montés dans le périphérique.
Emre Kilinc Arslan
0

Pour accéder aux fichiers de ma carte SD , sur mon HTC One X (Android), j'utilise ce chemin:

file:///storage/sdcard0/folder/filename.jpg

Notez le triple "/"!

Utilisateur HTC
la source
-1

Sur Galaxy S3 Android 4.3, le chemin que j'utilise est ./storage/extSdCard/Card/ et il fait le travail. J'espère que ça aide,

CLEMENT DUPUIS
la source
-1

Les étapes suivantes ont fonctionné pour moi. Il vous suffit d'écrire ces lignes:

String sdf = new String(Environment.getExternalStorageDirectory().getName());
String sddir = new String(Environment.getExternalStorageDirectory().getPath().replace(sdf,""));

La première ligne donnera le nom du répertoire sd, et il vous suffit de l'utiliser dans la méthode replace pour la deuxième chaîne. La deuxième chaîne contiendra le chemin du sd interne et amovible (/ storage / dans mon cas) . J'avais juste besoin de ce chemin pour mon application mais vous pouvez aller plus loin si vous en avez besoin.

Envoyé
la source