Como posso obter o caminho do cartão SD externo para Android 4.0+?

93

Samsung Galaxy S3 tem um slot de cartão SD externo, que é montado /mnt/extSdCard.

Como posso obter esse caminho por algo assim Environment.getExternalStorageDirectory()?

Isso retornará mnt/sdcard, e não consigo encontrar a API para o cartão SD externo. (Ou armazenamento USB removível em alguns tablets.)

Romulus Urakagi Ts'ai
fonte
Por que voce quer isso? É muito desaconselhável no Android fazer coisas assim. Talvez se você compartilhar sua motivação, possamos apontar a melhor prática para esse tipo de coisa.
rharter
2
Quando o usuário muda o telefone, conecte o cartão SD no novo telefone, e no primeiro telefone é / sdcard, no segundo telefone é / mnt / extSdCard, qualquer coisa que use o caminho do arquivo falha. Preciso gerar um caminho real a partir de seu caminho relativo.
Romulus Urakagi Ts'ai
Agora, este tópico é antigo, mas isso pode ajudar. você deve usar este método. System.getenv (); consulte o projeto Environment3 para acessar todos os armazenamentos que estão conectados ao seu dispositivo. github.com/omidfaraji/Environment3
Omid Faraji
Possível duplicata de Encontrar um local externo do cartão SD
Brian Rogers
Aqui está minha solução que funciona até Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC

Respostas:

58

Tenho uma variação de uma solução que encontrei aqui

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

O método original foi testado e trabalhado com

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

Não tenho certeza de qual versão do Android eles estavam quando foram testados.

Eu testei minha versão modificada com

  • Moto Xoom 4.1.2 (estoque)
  • Galaxy Nexus (cyanogenmod 10) usando um cabo otg
  • HTC Incredible (cyanogenmod 7.2) este retornou tanto o interno quanto o externo. Este dispositivo é meio esquisito, pois seu interno praticamente não é usado, já que getExternalStorage () retorna um caminho para o sdcard.

e alguns dispositivos de armazenamento único que usam um cartão SD como seu armazenamento principal

  • HTC G1 (cianogêniomod 6.1)
  • HTC G1 (estoque)
  • HTC Vision / G2 (estoque)

Com exceção do incrível, todos esses dispositivos retornaram apenas seu armazenamento removível. Provavelmente, há algumas verificações extras que devo fazer, mas isso é pelo menos um pouco melhor do que qualquer solução que encontrei até agora.

Gnatônico
fonte
3
Acho que sua solução fornecerá um nome de pasta inválido se o original contiver letras maiúsculas como / mnt / SdCard
Eugene Popovich
1
Eu testei em alguns dispositivos Motorola, Samsung e LG e funcionou perfeitamente.
pepedeutico
2
@KhawarRaza, este método parou de funcionar a partir do kitkat. Use o método de Dmitriy Lozenko, com isso como uma alternativa se você estiver oferecendo suporte a dispositivos pré-ics.
Gnatônico
1
Funciona no HUAWEI Honor 3c. Obrigado.
li2 de
2
isso está retornando / mnt em vez de / storage em 4.0+ para mim em um Galaxy Note 3
ono
54

Eu encontrei uma maneira mais confiável de obter caminhos para todos os SD-CARDs no sistema. Isso funciona em todas as versões do Android e caminhos de retorno para todos os armazenamentos (incluindo emulados).

Funciona corretamente em todos os meus dispositivos.

PS: Baseado no código fonte da classe 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
fonte
5
O que DIR_SEPORATOR deveria ser?
cYrixmorten
1
@cYrixmorten é fornecido no topo do código .. é um Patternpara/
Ankit Bansal
Alguém testou esta solução no Lolipop?
lenrok258
Tentei esse método no lenovo P780.Ele não está funcionando.
Satheesh Kumar
2
Funciona até Lollipop; não funciona no Marshmallow (Galaxy Tab A w / 6.0.1) onde System.getenv("SECONDARY_STORAGE")retorna /storage/sdcard1. Esse local não existe, mas o local real é /storage/42CE-FD0A, onde 42CE-FD0A é o número de série do volume da partição formatada.
DaveAlden
32

Acho que para usar o cartão SD externo, você precisa usar este:

new File("/mnt/external_sd/")

OU

new File("/mnt/extSdCard/")

no seu caso...

em substituição de Environment.getExternalStorageDirectory()

Funciona para mim. Você deve verificar o que está no diretório mnt primeiro e trabalhar a partir daí.


Você deve usar algum tipo de método de seleção para escolher qual cartão SD usar:

File storageDir = new File("/mnt/");
if(storageDir.isDirectory()){
    String[] dirList = storageDir.list();
    //TODO some type of selecton method?
}
FabianCook
fonte
1
Bem, na verdade eu quero um método em vez de um caminho de código fixo, mas o / mnt / list deve servir. Obrigado.
Romulus Urakagi Ts'ai
Sem problemas. Tenha uma opção nas configurações de onde você escolhe o caminho e, em seguida, use o método para recuperá-lo.
FabianCook
10
Infelizmente, o cartão SD externo pode estar em uma pasta diferente de / mnt. Por exemplo, no Samsung GT-I9305 é / storage / extSdCard enquanto o cartão SD embutido tem o caminho / storage / sdcard0
iseeall
2
Bem, então você obteria a localização do sdcard e, em seguida, obteria seu diretório pai.
FabianCook
Qual será o caminho do cartão SD externo (para pendrive conectado ao tablet via cabo OTG) para Android 4.0+?
osimer pothe
15

Para recuperar todos os armazenamentos externos (sejam eles cartões SD ou armazenamentos internos não removíveis ), você pode usar o seguinte 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");
            }
        }
    }
}

Como alternativa, você pode usar System.getenv ("EXTERNAL_STORAGE") para recuperar o diretório de armazenamento externo primário (por exemplo, "/ storage / sdcard0" ) e System.getenv ("SECONDARY_STORAGE") para recuperar a lista de todos os diretórios secundários (por exemplo, " / storage / extSdCard: / storage / UsbDriveA: / storage / UsbDriveB " ). Lembre-se que, também neste caso, você pode querer filtrar a lista de diretórios secundários para excluir as unidades USB.

Em qualquer caso, observe que usar caminhos embutidos em código é sempre uma abordagem ruim (especialmente quando cada fabricante pode alterá-lo como quiser).

Paolo Rovelli
fonte
System.getenv ("SECONDARY_STORAGE") NÃO funcionou para dispositivos USB conectados por meio de cabo OTG no Nexus 5 e Nexus 7.
Khawar Raza
Esta é minha solução que funciona até Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC
14

Eu estava usando a solução de Dmitriy Lozenko até verificar em um Asus Zenfone2 , Marshmallow 6.0.1 e a solução não está funcionando. A solução falhou ao obter EMULATED_STORAGE_TARGET , especificamente para o caminho microSD, ou seja: / storage / F99C-10F4 / . Editei o código para obter os caminhos de raiz emulados diretamente dos caminhos de aplicativos emulados context.getExternalFilesDirs(null);e adicionar mais caminhos físicos específicos de modelos de telefone conhecidos .

Para facilitar a nossa vida, fiz uma biblioteca aqui . Você pode usá-lo via sistema de compilação gradle, maven, sbt e leiningen.

Se você gosta da maneira antiga, também pode copiar e colar o arquivo diretamente daqui , mas não saberá se há uma atualização no futuro sem verificar manualmente.

Se você tiver alguma dúvida ou sugestão, por favor me avise

HendraWD
fonte
1
Não usei muito isso, mas funciona bem para o meu problema.
David
1
Ótima solução! No entanto, uma observação - se o cartão SD for removido quando o aplicativo estiver ativo, context.getExternalFilesDirs(null)retornará nullpara um dos arquivos e haverá uma exceção no ciclo seguinte. Eu sugiro adicionar if (file == null) continue;como a primeira linha do loop.
Koger
1
@Koger obrigado pela sugestão, eu adicionei e
corrigi
13

Boas notícias! No KitKat agora há uma API pública para interagir com esses dispositivos de armazenamento compartilhados secundários.

Os novos métodos Context.getExternalFilesDirs()e Context.getExternalCacheDirs()podem retornar vários caminhos, incluindo dispositivos primários e secundários. Você pode então iterar sobre eles e verificar Environment.getStorageState()e File.getFreeSpace()determinar o melhor lugar para armazenar seus arquivos. Esses métodos também estão disponíveis emContextCompat biblioteca support-v4.

Observe também que, se estiver interessado apenas em usar os diretórios retornados por Context, você não precisará mais das permissões READ_ou WRITE_EXTERNAL_STORAGE. No futuro, você sempre terá acesso de leitura / gravação a esses diretórios, sem a necessidade de permissões adicionais.

Os aplicativos também podem continuar trabalhando em dispositivos mais antigos ao final da vida útil de sua solicitação de permissão, como esta:

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="18" />
Jeff Sharkey
fonte
Obrigado. Fiz um código que segue o que você escreveu e acho que funciona bem para encontrar todos os caminhos de cartões SD. Você pode dar uma olhada? Aqui: stackoverflow.com/a/27197248/878126. BTW, você não deve usar "getFreeSpace" porque teoricamente, o espaço livre pode mudar durante a execução.
desenvolvedor Android de
10

Fiz o seguinte para obter acesso a todos os cartões SD externos.

Com:

File primaryExtSd=Environment.getExternalStorageDirectory();

você obtém o caminho para o SD externo primário Em seguida:

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

você obtém o diretório pai do armazenamento externo primário e também é o pai de todos os sd externos. Agora, você pode listar todo o armazenamento e selecionar aquele que deseja.

Espero que seja útil.

valenta
fonte
Esta deve ser a resposta aceita, e obrigado, é útil.
Meanman
9

Veja como obtenho a lista de caminhos de cartão SD (excluindo o armazenamento externo primário):

  /**
   * 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;
      }
    }
desenvolvedor android
fonte
externalCacheDirs[0].getParentFile().getParentFile().getParentFile().getParentFile().getAbsolutePath()Isto é uma receita para o desastre. Se você estiver fazendo isso, você também pode apenas codificar permanentemente o caminho que deseja, pois se você obtiver um diretório de cache com qualquer coisa diferente de 4 pais, obterá NullPointerException ou o caminho errado.
rharter
@rharter Correto. Este problema também foi corrigido. Por favor, dê uma olhada.
desenvolvedor Android de
Os documentos ( developer.android.com/reference/android/content/… ) dizem "Os dispositivos de armazenamento externos devolvidos aqui são considerados uma parte permanente do dispositivo, incluindo armazenamento externo emulado e slots de mídia física, como cartões SD em uma bateria compartimento. Os caminhos retornados não incluem dispositivos temporários, como unidades flash USB. " Parece que eles também não incluem slots para cartões SD, ou seja, os locais onde você pode remover um cartão SD sem entrar na bateria. Difícil dizer, no entanto.
LarsH
1
@LarsH Bem, eu mesmo testei no SGS3 (e no cartão SD real), então acho que deve funcionar em outros dispositivos também.
desenvolvedor do Android
5

Obrigado pelas pistas fornecidas por vcs, principalmente @SmartLemon, encontrei a solução. Caso outra pessoa precise, coloco minha solução final aqui (para encontrar o primeiro cartão SD externo listado):

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

Se não houver nenhum cartão SD externo, ele retornará o armazenamento integrado. Vou usá-lo se o sdcard não existir, talvez seja necessário alterá-lo.

rml
fonte
1
este lançamento de null no Android Versão 19. rootDir.listFiles () retorna null. Eu testei com nexus 7, emulador e nota de galáxia.
Manu Zi
3

consulte meu código, espero que seja útil para você:

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

Na verdade, em alguns dispositivos, o nome padrão do sdcard externo é mostrado como extSdCarde para outros é sdcard1.

Este trecho de código ajuda a descobrir o caminho exato e ajuda a recuperar o caminho do 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
fonte
Isso me ajudou imensamente.
Droid Chris
Existem alguns dispositivos que usam "/ storage / external_sd" (LG G3 KitKat), o Marshmallow tem um esquema diferente, o armazenamento interno no Nexus 5X usando Android 6.0 é "/ mnt / sdcard" e o SDcard externo é encontrado em "/ storage / XXXX-XXXX "
AB
2

Sim. Diferentes fabricantes usam diferentes nomes de cartões SD como no Samsung Tab 3 seu extsd, e outros dispositivos Samsung usam sdcard como este fabricante diferente usa nomes diferentes.

Eu tinha os mesmos requisitos que você. então eu criei um exemplo de amostra para você do meu projeto vá para este link Exemplo de seletor de diretório Android que usa a biblioteca androi-dirchooser. Este exemplo detecta o SDcard e lista todas as subpastas e também detecta se o dispositivo tem mais de um SDcard.

Parte do código se parece com isto Para um exemplo completo, vá para o link Android Directory Chooser

/**
* 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
fonte
2

Esta solução (montada a partir de outras respostas a esta pergunta) lida com o fato (conforme mencionado por @ono) que não System.getenv("SECONDARY_STORAGE")é útil com o Marshmallow.

Testado e trabalhando em:

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

    /**
     * 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
fonte
A condição rawSecondaryStoragesStr.contains(path)pode permitir que o armazenamento interno também seja adicionado; podemos remover melhor essa condição. Enfrentei esse problema, pois meu dispositivo retorna o diretório de armazenamento interno com System.getenv("SECONDARY_STORAGE"), mas retorna o caminho do cartão de memória externa com System.getenv(EXTERNAL_STORAGE)em KitKat; parece que diferentes fornecedores implementaram isso de maneira diferente, sem qualquer padrão.
Gokul NC
Esta é minha solução que funciona até Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC
1

Em alguns dispositivos (por exemplo, samsung galaxy sII), o cartão de memória interno pode estar em vfat. Neste caso, use o último código referente, obtemos o cartão de memória interno do caminho (/ mnt / sdcad), mas nenhum cartão externo. Código de referência abaixo 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
fonte
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
fonte
0

Tenho certeza de que este código certamente resolverá seus problemas ... Isso está funcionando bem para mim ... \

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
fonte
Environment.getExternalStorageDirectory () não deve fornecer a localização exata de seu sdcard externo ou localização de USB. Basta analisar a localização "/ proc / mounts" e / dev / fuse / storage / sdcard1 "fornecerá a localização se seu sdcard está correto montado ou não é o mesmo para usb também. Desta forma, você pode obtê-lo facilmente ... Atenciosamente
Sam
0

isso não é verdade. / mnt / sdcard / external_sd pode existir mesmo se o cartão SD não estiver montado. seu aplicativo irá travar quando você tentar gravar em / mnt / sdcard / external_sd quando não estiver montado.

você precisa verificar se o cartão SD foi montado primeiro usando:

boolean isSDPresent = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
Marcin Skoczylas
fonte
7
Isso não faz sentido quando getExternalStorageState()retorna o armazenamento interno em vez do cartão SD externo, eu acho.
Romulus Urakagi Ts'ai
Eles fornecem uma função para obter um cartão SD externo agora? Não consigo encontrar nenhuma pista depois de procurar por uma hora. Se for esse o caso, então isso é tão estranho depois de tantas versões atualizadas.
rml
Quando você disse "isso não é verdade", a que se referia? Eu sei, isso foi há 2,5 anos ...
LarsH 01 de
0
 String path = Environment.getExternalStorageDirectory()
                        + File.separator + Environment.DIRECTORY_PICTURES;
                File dir = new File(path);
sarra srairi
fonte
4
Não funciona. Ele retorna o caminho do armazenamento interno. ou seja, / storage / emulado / 0
0

Você pode usar algo como - Context.getExternalCacheDirs () ou Context.getExternalFilesDirs () ou Context.getObbDirs (). Eles fornecem diretórios específicos ao aplicativo em todos os dispositivos de armazenamento externos onde o aplicativo pode armazenar seus arquivos.

Portanto, algo como isso - Context.getExternalCacheDirs () [i] .getParentFile (). GetParentFile (). GetParentFile (). GetParent () pode obter o caminho raiz de dispositivos de armazenamento externos.

Eu sei que esses comandos têm um propósito diferente, mas outras respostas não funcionaram para mim.

Este link me deu boas dicas - https://possiblemobile.com/2014/03/android-external-storage/

Sharath Holla
fonte
0

System.getenv("SECONDARY_STORAGE")retorna nulo para Marshmallow. Esta é outra maneira de encontrar todos os diretórios externos. Você pode verificar se é removível, o que determina se 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
fonte
0

Eu tentei as soluções fornecidas por Dmitriy Lozenko e Gnathonic em meu Samsung Galaxy Tab S2 (modelo: T819Y), mas nenhuma me ajudou a recuperar o caminho para um diretório de cartão SD externo. mounta execução do comando continha o caminho necessário para o diretório externo do cartão SD (ou seja, / Storage / A5F9-15F4), mas não correspondia à expressão regular, portanto, não foi retornado. Não entendi o mecanismo de nomenclatura de diretório seguido por Samsung. Por que eles se desviam dos padrões (extsdcard) e aparecem com algo realmente suspeito, como no meu caso (ou seja, / Storage / A5F9-15F4) . Está faltando alguma coisa? De qualquer forma, seguindo as mudanças na expressão regular de Gnatônico solução me ajudou a obter um diretório 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;

Não tenho certeza se esta é uma solução válida e se dará resultados para outros tablets Samsung, mas corrigiu meu problema por enquanto. A seguir está outro método para recuperar o caminho removível do cartão SD no Android (v6.0). Eu testei o método com o Android Marshmallow e funciona. A abordagem usada é muito básica e certamente funcionará com outras versões também, mas o teste é obrigatório. Algumas dicas sobre isso serão úteis:

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, compartilhe se você tiver qualquer outra abordagem para lidar com esse problema. obrigado

Abdul Rehman
fonte
Esta é minha solução que funciona até Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC
Na verdade, tenho o mesmo problema com você no Asus Zenfone 2, mas alterei o código de Dmitriy Lozenko para resolver o problema stackoverflow.com/a/40582634/3940133
HendraWD
@HendraWD a solução que você está sugerindo pode não funcionar com o Android Marshmallow (6.0), pois o suporte para variáveis ​​de ambiente foi removido dele. Não me lembro exatamente, mas acho que tentei.
Abdul Rehman
@GokulNC, vou tentar. parece legítimo :)
Abdul Rehman
@GokulNC sim, é por isso que eu uso context.getExternalFilesDirs (null); em vez de usar caminhos físicos diretamente quando a versão for> Marshmallow
HendraWD
0
String secStore = System.getenv("SECONDARY_STORAGE");

File externalsdpath = new File(secStore);

Isso obterá o caminho do armazenamento secundário externo do SD.

Kenneth Villamin
fonte
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
fonte
Isso é uma alternativa para iniciantes, pelo menos, obtendo stringList de todos os drivers montados no dispositivo.
Emre Kilinc Arslan
0

Para acessar arquivos no meu cartão SD , no meu HTC One X (Android), eu uso este caminho:

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

Observe o triplo "/"!

Usuário HTC
fonte
-1

No Galaxy S3 Android 4.3, o caminho que uso é ./storage/extSdCard/Card/ e ele faz o trabalho. Espero que ajude,

CLEMENT DUPUIS
fonte
-1

As etapas a seguir funcionaram para mim. Você só precisa escrever estas linhas:

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

A primeira linha fornecerá o nome do diretório sd, e você só precisa usá-lo no método de substituição para a segunda string. A segunda string conterá o caminho para o sd interno e removível (/ storage / no meu caso) . Eu só precisava desse caminho para meu aplicativo, mas você pode ir mais longe se precisar.

Enviado
fonte