¿Cómo puedo obtener la ruta de la tarjeta SD externa para Android 4.0+?

94

Samsung Galaxy S3 tiene una ranura para tarjeta SD externa, que está montada en /mnt/extSdCard.

¿Cómo puedo obtener este camino por algo como Environment.getExternalStorageDirectory()?

Esto volverá mnt/sdcardy no puedo encontrar la API para la tarjeta SD externa. (O almacenamiento USB extraíble en algunas tabletas).

Romulus Urakagi Ts'ai
fuente
¿Por qué quieres esto? Es muy desaconsejable en Android hacer cosas como esta. Quizás, si comparte su motivación, podamos orientarle en la dirección de las mejores prácticas para este tipo de cosas.
rharter
2
Cuando su usuario cambie su teléfono, conecte la tarjeta SD al nuevo teléfono, y en el primer teléfono es / sdcard, en el segundo teléfono es / mnt / extSdCard, cualquier cosa que use la ruta del archivo se bloquea. Necesito generar una ruta real a partir de su ruta relativa.
Romulus Urakagi Ts'ai
Ahora este tema es antiguo, pero esto puede ayudar. debe utilizar este método. System.getenv (); consulte el proyecto Environment3 para acceder a todo el almacenamiento que está conectado a su dispositivo. github.com/omidfaraji/Environment3
Omid Faraji
Posible duplicado de Buscar una ubicación de tarjeta SD externa
Brian Rogers
Aquí está mi solución que funciona hasta Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC

Respuestas:

58

Tengo una variación de una solución que encontré aquí.

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

El método original fue probado y trabajado con

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

No estoy seguro de en qué versión de Android estaban cuando se probaron.

He probado mi versión modificada con

  • Moto Xoom 4.1.2 (stock)
  • Galaxy Nexus (cyanogenmod 10) con un cable otg
  • HTC Incredible (cyanogenmod 7.2) esto devolvió tanto el interno como el externo. Este dispositivo es un poco extraño en el sentido de que su interno no se usa en gran medida, ya que getExternalStorage () devuelve una ruta a la tarjeta sd.

y algunos dispositivos de almacenamiento únicos que utilizan una tarjeta SD como almacenamiento principal

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

Excepto el Increíble, todos estos dispositivos solo devolvieron su almacenamiento extraíble. Probablemente deba hacer algunas comprobaciones adicionales, pero esto es al menos un poco mejor que cualquier solución que haya encontrado hasta ahora.

Gnatónico
fuente
3
Creo que su solución dará un nombre de carpeta no válido si la original contiene letras mayúsculas como / mnt / SdCard
Eugene Popovich
1
Probé en algunos dispositivos Motorola, Samsung y LG y funcionó a la perfección.
pepedeutico
2
@KhawarRaza, este método dejó de funcionar a partir de kitkat. Utilice el método de Dmitriy Lozenko, con esto como alternativa si está admitiendo dispositivos pre-ics.
Gnathonic
1
Funciona en HUAWEI Honor 3c. Gracias.
li2
2
esto se vuelve lugar / mnt / almacenamiento en 4.0+ para mí en un Galaxy Note 3
ono
54

Encontré una forma más confiable de obtener rutas a todas las SD-CARD en el sistema. Esto funciona en todas las versiones de Android y devuelve las rutas a todos los almacenamientos (incluido el emulado).

Funciona correctamente en todos mis dispositivos.

PD: Basado en el código fuente de la clase Environment.

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
fuente
5
¿Qué se suponía que era DIR_SEPORATOR?
cYrixmorten
1
@cYrixmorten se proporciona en la parte superior del código ... es Patternpara/
Ankit Bansal
¿Alguien probó esta solución en Lolipop?
lenrok258
Probé este método en lenovo P780 y no funciona.
Satheesh Kumar
2
Funciona hasta Lollipop; no funciona en Marshmallow (Galaxy Tab A w / 6.0.1) donde System.getenv("SECONDARY_STORAGE")regresa /storage/sdcard1. Esa ubicación no existe, pero la ubicación real es /storage/42CE-FD0A, donde 42CE-FD0A es el número de serie del volumen de la partición formateada.
DaveAlden
32

Supongo que para usar la tarjeta sd externa necesitas usar esto:

new File("/mnt/external_sd/")

O

new File("/mnt/extSdCard/")

en tu caso...

en reemplazo de Environment.getExternalStorageDirectory()

Funciona para mi. Primero debe verificar lo que está en el directorio mnt y trabajar desde allí.


Debería utilizar algún tipo de método de selección para elegir qué sdcard utilizar:

File storageDir = new File("/mnt/");
if(storageDir.isDirectory()){
    String[] dirList = storageDir.list();
    //TODO some type of selecton method?
}
FabianCook
fuente
1
Bueno, de hecho, quiero un método en lugar de una ruta de código rígida, pero la lista / mnt / debería estar bien. Gracias.
Romulus Urakagi Ts'ai
No hay problema. Tenga una opción en la configuración de la que elija la ruta, luego use el método para recuperarla.
FabianCook
10
Desafortunadamente, la tarjeta SD externa puede estar en una carpeta diferente a / mnt. Por ejemplo, en Samsung GT-I9305 es / storage / extSdCard mientras que la tarjeta SD incorporada tiene la ruta / storage / sdcard0
iseeall
2
Bueno, entonces obtendría la ubicación de la tarjeta SD, luego obtendría su directorio principal.
FabianCook
¿Cuál será la ruta de la tarjeta SD externa (para pendrive conectado a la tableta mediante un cable OTG) para Android 4.0+?
Osimer pothe
15

Para recuperar todos los almacenamientos externos (ya sean tarjetas SD o almacenamientos internos no extraíbles ), puede utilizar el siguiente código:

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

Alternativamente, puede usar System.getenv ("EXTERNAL_STORAGE") para recuperar el directorio de almacenamiento externo primario (por ejemplo, "/ storage / sdcard0" ) y System.getenv ("SECONDARY_STORAGE") para recuperar la lista de todos los directorios secundarios (por ejemplo, " / almacenamiento / extSdCard: / almacenamiento / UsbDriveA: / almacenamiento / UsbDriveB " ). Recuerde que, también en este caso, es posible que desee filtrar la lista de directorios secundarios para excluir las unidades USB.

En cualquier caso, tenga en cuenta que el uso de rutas codificadas de forma rígida siempre es un mal enfoque (especialmente cuando todos los fabricantes pueden cambiarlo a su gusto).

Paolo Rovelli
fuente
System.getenv ("SECONDARY_STORAGE") NO funcionó para dispositivos USB conectados a través de un cable OTG en Nexus 5 y Nexus 7.
Khawar Raza
Aquí está mi solución que funciona hasta Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC
14

Estaba usando la solución de Dmitriy Lozenko hasta que comprobé un Asus Zenfone2 , Marshmallow 6.0.1 y la solución no funciona. La solución falló al obtener EMULATED_STORAGE_TARGET , específicamente para la ruta microSD, es decir: / storage / F99C-10F4 / . Edité el código para obtener las rutas raíz emuladas directamente desde las rutas de la aplicación emuladas context.getExternalFilesDirs(null);y agregué rutas físicas específicas del modelo de teléfono más conocidas .

Para hacer nuestra vida más fácil, hice una biblioteca aquí . Puede usarlo a través del sistema de compilación gradle, maven, sbt y leiningen.

Si le gusta la forma antigua, también puede copiar y pegar el archivo directamente desde aquí , pero no sabrá si hay una actualización en el futuro sin verificarlo manualmente.

Si tiene alguna pregunta o sugerencia, hágamelo saber.

HendraWD
fuente
1
No lo he usado mucho, pero funciona bien para mi problema.
David
1
¡Gran solución! Sin embargo, una nota: si se quita la tarjeta SD cuando la aplicación está activa, context.getExternalFilesDirs(null)volverá nullpara uno de los archivos y habrá una excepción en el siguiente ciclo. Sugiero agregar if (file == null) continue;como la primera línea en el ciclo.
Koger
1
@Koger, gracias por la sugerencia, la agregué y solucioné un error tipográfico en mi respuesta
HendraWD
13

¡Buenas noticias! En KitKat ahora hay una API pública para interactuar con estos dispositivos secundarios de almacenamiento compartido.

Los métodos nuevos Context.getExternalFilesDirs()y Context.getExternalCacheDirs()pueden devolver varias rutas, incluidos los dispositivos primarios y secundarios. Luego, puede iterar sobre ellos y verificar Environment.getStorageState()y File.getFreeSpace()determinar el mejor lugar para almacenar sus archivos. Estos métodos también están disponibles ContextCompaten la biblioteca support-v4.

También tenga en cuenta que si solo está interesado en usar los directorios devueltos por Context, ya no necesita los permisos READ_o WRITE_EXTERNAL_STORAGE. En el futuro, siempre tendrá acceso de lectura / escritura a estos directorios sin necesidad de permisos adicionales.

Las aplicaciones también pueden seguir funcionando en dispositivos más antiguos al finalizar su solicitud de permiso de esta manera:

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="18" />
Jeff Sharkey
fuente
Gracias. Hice un código que sigue lo que ha escrito, y creo que funciona bien para encontrar todas las rutas de las tarjetas SD. Puedes echar un vistazo ? Aquí: stackoverflow.com/a/27197248/878126. Por cierto, no debería utilizar "getFreeSpace" porque, en teoría, el espacio libre puede cambiar durante el tiempo de ejecución.
desarrollador de Android
10

Hice lo siguiente para acceder a todas las tarjetas SD externas.

Con:

File primaryExtSd=Environment.getExternalStorageDirectory();

obtienes la ruta a la SD externa primaria Luego con:

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

obtiene el directorio principal del almacenamiento externo primario, y también es el principal de todos los sd externos. Ahora, puede enumerar todo el almacenamiento y seleccionar el que desee.

Espero que sea útil.

valenta
fuente
Esta debería ser la respuesta aceptada y gracias, es útil.
Meanman
9

Así es como obtengo la lista de rutas de tarjetas SD (excluyendo el almacenamiento externo primario):

  /**
   * 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;
      }
    }
desarrollador de Android
fuente
externalCacheDirs[0].getParentFile().getParentFile().getParentFile().getParentFile().getAbsolutePath()Esta es una receta para el desastre. Si está haciendo esto, también puede codificar la ruta que desea, ya que, si obtiene un directorio de caché con cualquier cosa que no sea 4 padres, obtendrá NullPointerException o la ruta incorrecta.
rharter
@rharter Correcto. También se solucionó este problema. Por favor échale un vistazo.
desarrollador de Android
Los documentos ( developer.android.com/reference/android/content/… ) dicen "Los dispositivos de almacenamiento externo devueltos aquí se consideran una parte permanente del dispositivo, incluido el almacenamiento externo emulado y las ranuras para medios físicos, como las tarjetas SD en una batería compartimento. Las rutas devueltas no incluyen dispositivos transitorios, como unidades flash USB ". Parece que tampoco incluyen ranuras para tarjetas SD, es decir, los lugares donde puedes quitar una tarjeta SD sin pasar por debajo de la batería. Aunque es difícil de decir.
LarsH
1
@LarsH Bueno, lo he probado yo mismo en SGS3 (y en una tarjeta SD real), así que creo que también debería funcionar en otros dispositivos.
desarrollador de Android
5

Gracias por las pistas proporcionadas por ustedes, especialmente @SmartLemon, tengo la solución. En caso de que alguien más lo necesite, pongo mi solución final aquí (para encontrar la primera tarjeta SD externa listada):

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

Si no hay una tarjeta SD externa allí, devuelve el almacenamiento a bordo. Lo usaré si la tarjeta sd no existe, es posible que deba cambiarla.

rml
fuente
1
esto arroja un valor nulo en la versión 19 de Android. rootDir.listFiles () devuelve un valor nulo. Lo he probado con nexus 7, emulador y galaxy note.
Manu Zi
3

consulte mi código, espero que sea útil para usted:

    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
fuente
2

En realidad, en algunos dispositivos, el nombre predeterminado de la tarjeta SD externa se muestra como extSdCardy en otros sdcard1.

Este fragmento de código ayuda a encontrar esa ruta exacta y le ayuda a recuperar la ruta del dispositivo externo.

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
fuente
Esto me ayudó muchísimo.
Droid Chris
Hay algunos dispositivos que usan "/ storage / external_sd" (LG G3 KitKat), Marshmallow tiene un esquema diferente, el almacenamiento interno en Nexus 5X con Android 6.0 es "/ mnt / sdcard" y la tarjeta SD externa se encuentra en "/ storage / XXXX-XXXX "
AB
2

Si. Diferentes fabricantes usan diferentes nombres de tarjetas SD como en Samsung Tab 3, su extsd, y otros dispositivos Samsung usan tarjetas sd como este fabricante diferente usan diferentes nombres.

Tenía el mismo requisito que tú. así que he creado un ejemplo de muestra para usted de mi proyecto, vaya a este enlace Ejemplo de selector de directorio de Android que usa la biblioteca androi-dirchooser. Este ejemplo detecta la tarjeta SD y enumera todas las subcarpetas y también detecta si el dispositivo tiene más de una tarjeta SD.

Parte del código se ve así. Para un ejemplo completo, vaya al enlace Selector de directorio de 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
fuente
2

Esta solución (recopilada a partir de otras respuestas a esta pregunta) maneja el hecho (como lo menciona @ono) que System.getenv("SECONDARY_STORAGE")no sirve de nada con Marshmallow.

Probado y trabajando en:

  • 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
fuente
La condición rawSecondaryStoragesStr.contains(path)puede permitir que el almacenamiento interno también se agregue; mejor podemos eliminar esa condición. Me enfrenté a ese problema, ya que mi dispositivo devuelve el directorio de almacenamiento interno con System.getenv("SECONDARY_STORAGE"), pero devuelve la ruta de la tarjeta de memoria externa con System.getenv(EXTERNAL_STORAGE)en KitKat; parece que diferentes proveedores han implementado esto de manera diferente sin ningún maldito estándar ..
Gokul NC
Aquí está mi solución que funciona hasta Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC
1

En algunos dispositivos (por ejemplo, samsung galaxy sII), la tarjeta de memoria interna puede estar en vfat. En este caso use referir el último código, obtenemos la ruta de la tarjeta de memoria interna (/ mnt / sdcad) pero no la tarjeta externa. Consulte el código a continuación para resolver este problema.

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
fuente
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
fuente
0

Estoy seguro de que este código seguramente resolverá sus problemas ... Esto funciona bien para mí ... \

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
fuente
Environment.getExternalStorageDirectory () no debería darle la ubicación exacta de su tarjeta sd externa o ubicación usb. Simplemente analice la ubicación "/ proc / mounts" y luego / dev / fuse / storage / sdcard1 "le dará la ubicación si su tarjeta sd está correctamente montado o no lo mismo para usb también. De esta manera puede obtenerlo fácilmente .. Saludos ..
Sam
0

eso no es cierto. / mnt / sdcard / external_sd puede existir incluso si la tarjeta SD no está montada. su aplicación se bloqueará cuando intente escribir en / mnt / sdcard / external_sd cuando no esté montado.

primero debe verificar si la tarjeta SD está montada usando:

boolean isSDPresent = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
Marcin Skoczylas
fuente
7
getExternalStorageState()Creo que esto no tiene sentido cuando devuelve el almacenamiento interno en lugar de la tarjeta SD externa.
Romulus Urakagi Ts'ai
¿Proporcionan una función para obtener una tarjeta SD externa ahora? No puedo encontrar ninguna pista después de buscar durante una hora. Si este es el caso, entonces esto es tan extraño después de tantas versiones actualizadas.
rml
Cuando dijiste "eso no es cierto", ¿a qué te refieres? Lo sé, eso fue hace 2.5 años ...
LarsH
0
 String path = Environment.getExternalStorageDirectory()
                        + File.separator + Environment.DIRECTORY_PICTURES;
                File dir = new File(path);
sarra srairi
fuente
4
No funciona. Devuelve la ruta del almacenamiento interno. ie / storage /
0

Puede usar algo como: Context.getExternalCacheDirs () o Context.getExternalFilesDirs () o Context.getObbDirs (). Proporcionan directorios específicos de la aplicación en todos los dispositivos de almacenamiento externos donde la aplicación puede almacenar sus archivos.

Entonces, algo como esto: Context.getExternalCacheDirs () [i] .getParentFile (). GetParentFile (). GetParentFile (). GetParent () puede obtener la ruta raíz de los dispositivos de almacenamiento externos.

Sé que estos comandos tienen un propósito diferente, pero otras respuestas no funcionaron para mí.

Este enlace me dio buenos consejos: https://possiblemobile.com/2014/03/android-external-storage/

Sharath Holla
fuente
0

System.getenv("SECONDARY_STORAGE")devuelve nulo para Marshmallow. Esta es otra forma de encontrar todos los directorios externos. Puede verificar si es extraíble, lo que determina si es interno / externo

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
fuente
0

Probé las soluciones proporcionadas por Dmitriy Lozenko y Gnathonic en mi Samsung Galaxy Tab S2 (Modelo: T819Y) pero ninguna me ayudó a recuperar la ruta a un directorio de tarjeta SD externo. mountLa ejecución del comando contenía la ruta requerida al directorio externo de la tarjeta SD (es decir, / Storage / A5F9-15F4) pero no coincidía con la expresión regular, por lo que no se devolvió. No obtengo el mecanismo de nomenclatura de directorios seguido por Samsung. Por qué se desvían de los estándares (es decir, extsdcard) y se les ocurre algo realmente sospechoso como en mi caso (es decir, / Storage / A5F9-15F4) . ¿Hay algo que me esté perdiendo? De todos modos, siguiendo los cambios en la expresión regular de Gnathonic La solución me ayudó a obtener un directorio sdcard válido:

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;

No estoy seguro de si esta es una solución válida y si dará resultados para otras tabletas Samsung, pero ha solucionado mi problema por ahora. A continuación se muestra otro método para recuperar la ruta de la tarjeta SD extraíble en Android (v6.0). He probado el método con Android marshmallow y funciona. El enfoque utilizado en él es muy básico y seguramente también funcionará para otras versiones, pero las pruebas son obligatorias. Será útil tener una idea de ello:

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

Por favor, comparta si tiene algún otro enfoque para manejar este problema. Gracias

Abdul Rehman
fuente
Aquí está mi solución que funciona hasta Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC
De hecho, tengo el mismo problema contigo en Asus Zenfone 2, pero cambio el código de Dmitriy Lozenko para resolver el problema stackoverflow.com/a/40582634/3940133
HendraWD
@HendraWD, la solución que está sugiriendo podría no funcionar con Android Marshmallow (6.0), ya que se elimina la compatibilidad con las variables de entorno. No puedo recordar exactamente, pero creo que lo he intentado.
Abdul Rehman
@GokulNC Lo intentaré. parece legítimo :)
Abdul Rehman
@GokulNC sí, es por eso que uso context.getExternalFilesDirs (null); en lugar de utilizar rutas físicas directamente cuando la versión es> Marshmallow
HendraWD
0
String secStore = System.getenv("SECONDARY_STORAGE");

File externalsdpath = new File(secStore);

Esto obtendrá la ruta del almacenamiento secundario sd externo.

Kenneth Villamin
fuente
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
fuente
Alternativamente, esto es para principiantes que obtienen al menos stringList de todos los controladores montados en el dispositivo.
Emre Kilinc Arslan
0

Para acceder a los archivos en mi tarjeta SD , en mi HTC One X (Android), uso esta ruta:

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

¡Tenga en cuenta el triple "/"!

Usuario de HTC
fuente
-1

En Galaxy S3 Android 4.3, la ruta que uso es ./storage/extSdCard/Card/ y hace el trabajo. Espero eso ayude,

CLEMENT DUPUIS
fuente
-1

Los siguientes pasos funcionaron para mí. Solo necesitas escribir estas líneas:

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

La primera línea le dará el nombre del directorio sd, y solo necesita usarlo en el método de reemplazo para la segunda cadena. La segunda cadena contendrá la ruta para el sd interno y extraíble (/ almacenamiento / en mi caso) . Solo necesitaba esta ruta para mi aplicación, pero puedes ir más allá si la necesitas.

Enviado
fuente