Jak mogę uzyskać zewnętrzną ścieżkę karty SD dla Androida 4.0+?


94

Samsung Galaxy S3 posiada zewnętrzne gniazdo na kartę SD, do którego jest montowany /mnt/extSdCard.

Jak mogę uzyskać tę ścieżkę przez coś takiego Environment.getExternalStorageDirectory()?

To powróci mnt/sdcardi nie mogę znaleźć interfejsu API dla zewnętrznej karty SD. (Lub wymienna pamięć USB w niektórych tabletach.)


Dlaczego tego chcesz? Robienie takich rzeczy na Androidzie jest bardzo niewskazane. Być może, jeśli podzielasz swoją motywację, wskażemy Ci kierunek najlepszych praktyk w tego typu sytuacjach.
rharter

2
Kiedy użytkownik zmieni telefon, podłącz kartę SD do nowego telefonu, a na pierwszym telefonie jest to / sdcard, na drugim telefonie to / mnt / extSdCard, wszystko, co używa ścieżki do pliku, ulega awarii. Muszę wygenerować rzeczywistą ścieżkę na podstawie ścieżki względnej.
Romulus Urakagi Ts'ai

Ja teraz ten temat jest stary ale to może pomóc. powinieneś użyć tej metody. System.getenv (); zobacz projekt Środowisko3, aby uzyskać dostęp do wszystkich pamięci podłączonych do urządzenia. github.com/omidfaraji/Environment3
Omid


Oto moje rozwiązanie, które działa do Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC

Odpowiedzi:


58

Mam odmianę rozwiązania, które znalazłem tutaj

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

Oryginalna metoda została przetestowana i wykorzystana

  • Huawei X3 (zapas)
  • Galaxy S2 (standardowa)
  • Galaxy S3 (standardowy)

Nie jestem pewien, w której wersji Androida były one włączone podczas testowania.

Przetestowałem moją zmodyfikowaną wersję z

  • Moto Xoom 4.1.2 (standardowa)
  • Galaxy Nexus (cyanogenmod 10) za pomocą kabla otg
  • HTC Incredible (cyanogenmod 7.2) to zwróciło zarówno wewnętrzne, jak i zewnętrzne. To urządzenie jest trochę dziwne, ponieważ jego wewnętrzne w dużej mierze nie jest używane, ponieważ getExternalStorage () zwraca zamiast tego ścieżkę do sdcard.

i niektóre pojedyncze urządzenia pamięci masowej, które używają sdcard jako głównego magazynu

  • HTC G1 (cyjanogenmod 6.1)
  • HTC G1 (standardowy)
  • HTC Vision / G2 (standardowe)

Z wyjątkiem Incredible wszystkie te urządzenia zwróciły tylko wymienną pamięć masową. Prawdopodobnie powinienem przeprowadzić dodatkowe kontrole, ale jest to przynajmniej trochę lepsze niż jakiekolwiek rozwiązanie, które znalazłem do tej pory.


3
Myślę, że rozwiązanie poda nieprawidłową nazwę folderu, jeśli oryginalny zawiera wielkie litery, takie jak / mnt / SdCard
Eugene Popovich

1
Testowałem na niektórych urządzeniach Motorola, Samsung i LG i działało idealnie.
pepedeutico

2
@KhawarRaza, ta metoda przestała działać od KitKat. Użyj metody Dmitriya Lozenko, z tym jako rozwiązanie awaryjne, jeśli obsługujesz urządzenia pre-ics.
Gnathonic

1
Działa na HUAWEI Honor 3c. Dzięki.
li2

2
to jest powrót / mnt zamiast / magazynowania na wersji 4.0 lub nowszej dla mnie na Galaxy Note 3
Ono

54

Znalazłem bardziej niezawodny sposób na uzyskanie ścieżek do wszystkich kart SD w systemie. Działa to na wszystkich wersjach Androida i zwraca ścieżki do wszystkich magazynów (w tym emulowanych).

Działa poprawnie na wszystkich moich urządzeniach.

PS: Na podstawie kodu źródłowego klasy 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()]);
}

5
Czym miał być DIR_SEPORATOR?
cYrixmorten

1
@cYrixmorten jest dostarczany na szczycie kodu .. to a Patternfor/
Ankit Bansal

Czy ktoś testował to rozwiązanie na Lolipopie?
lenrok258

Wypróbowałem tę metodę na lenovo P780.Nie działa.
Satheesh Kumar

2
Działa aż do Lollipopa; nie działa na Marshmallow (Galaxy Tab A w / 6.0.1), gdzie System.getenv("SECONDARY_STORAGE")zwraca /storage/sdcard1. Ta lokalizacja nie istnieje, ale rzeczywista lokalizacja to miejsce /storage/42CE-FD0A, w którym 42CE-FD0A to numer seryjny woluminu sformatowanej partycji.
DaveAlden

32

Myślę, że aby użyć zewnętrznej karty SD, musisz użyć tego:

new File("/mnt/external_sd/")

LUB

new File("/mnt/extSdCard/")

w Twoim przypadku...

w miejsce Environment.getExternalStorageDirectory()

Pracuje dla mnie. Powinieneś najpierw sprawdzić, co jest w katalogu mnt i zacząć od tego miejsca.


Powinieneś użyć jakiejś metody wyboru, aby wybrać, której karty SD użyć:

File storageDir = new File("/mnt/");
if(storageDir.isDirectory()){
    String[] dirList = storageDir.list();
    //TODO some type of selecton method?
}

1
Cóż, wolę raczej metodę niż ścieżkę do twardego kodu, ale lista / mnt / powinna być w porządku. Dziękuję Ci.
Romulus Urakagi Ts'ai

Nie ma problemu. Miej opcję w ustawieniach, z których wybierasz ścieżkę, a następnie użyj metody, aby ją odzyskać.
FabianCook,

10
Niestety zewnętrzna karta SD może znajdować się w innym folderze niż / mnt. Np. W Samsung GT-I9305 to / storage / extSdCard, podczas gdy wbudowana karta SD ma ścieżkę / pamięć / sdcard0
iseeall

2
Cóż, możesz uzyskać lokalizację sdcard, a następnie pobrać jej katalog nadrzędny.
FabianCook

Jaka będzie ścieżka zewnętrznej karty SD (dla pendrive'a podłączonego do tabletu kablem OTG) dla Androida 4.0+?
osimer pothe

15

Aby pobrać wszystkie zewnętrzne magazyny (niezależnie od tego, czy są to karty SD, czy wewnętrzne niewymienne pamięci ), możesz użyć następującego kodu:

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

Alternatywnie możesz użyć System.getenv („EXTERNAL_STORAGE”), aby pobrać podstawowy katalog zewnętrznej pamięci masowej (np. „/ Storage / sdcard0” ) i System.getenv („SECONDARY_STORAGE”), aby pobrać listę wszystkich drugorzędnych katalogów (np. „ / storage / extSdCard: / storage / UsbDriveA: / storage / UsbDriveB ” ). Pamiętaj, że również w tym przypadku możesz chcieć przefiltrować listę katalogów pomocniczych, aby wykluczyć dyski USB.

W każdym razie należy pamiętać, że korzystanie ze ścieżek zakodowanych na stałe jest zawsze złym podejściem (szczególnie, gdy każdy producent może to zmienić zgodnie z życzeniem).


System.getenv („SECONDARY_STORAGE”) NIE działał na urządzeniach USB podłączonych przez kabel OTG na Nexusie 5 i Nexusie 7.
Khawar Raza,

Oto moje rozwiązanie, które działa do Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC

14

Używałem Dmitriy Lozenko „s rozwiązanie, dopóki nie sprawdził na Asus Zenfone2 , Marshmallow 6.0.1 i rozwiązanie nie działa. Rozwiązanie nie powiodło się podczas pobierania EMULATED_STORAGE_TARGET , szczególnie dla ścieżki microSD, tj .: / storage / F99C-10F4 / . Edytowałem kod, aby uzyskać emulowane ścieżki główne bezpośrednio z emulowanych ścieżek aplikacji context.getExternalFilesDirs(null);i dodać bardziej znane ścieżki fizyczne specyficzne dla modelu telefonu .

Aby ułatwić nam życie, stworzyłem tutaj bibliotekę . Możesz go używać za pomocą systemu kompilacji gradle, maven, sbt i leiningen.

Jeśli podoba Ci się staroświecki sposób, możesz również skopiować i wkleić plik bezpośrednio stąd , ale nie będziesz wiedział, czy w przyszłości będzie aktualizacja, bez sprawdzania jej ręcznie.

Jeśli masz jakieś pytania lub sugestie, daj mi znać


1
Nie używałem tego intensywnie, ale działa to dobrze w moim problemie.
David

1
Świetne rozwiązanie! Jedna uwaga - jeśli karta SD zostanie wyjęta, gdy aplikacja jest aktywna, context.getExternalFilesDirs(null)wróci nullpo jeden z plików i będzie wyjątek w następnej pętli. Proponuję dodać if (file == null) continue;jako pierwszą linię w pętli.
Koger

1
@Koger dziękuję za sugestię, dodałem ją i
poprawiłem

13

Dobre wieści! W KitKat jest teraz publiczny interfejs API do interakcji z tymi dodatkowymi współdzielonymi urządzeniami magazynującymi.

Nowe metody Context.getExternalFilesDirs()i Context.getExternalCacheDirs()mogą zwracać wiele ścieżek, w tym zarówno urządzenia podstawowe, jak i pomocnicze. Następnie możesz je iterować, sprawdzać Environment.getStorageState()i File.getFreeSpace()określać najlepsze miejsce do przechowywania plików. Te metody są również dostępne ContextCompatw bibliotece support-v4.

Pamiętaj również, że jeśli jesteś zainteresowany tylko używaniem katalogów zwróconych przez Context, nie potrzebujesz już uprawnień READ_lub WRITE_EXTERNAL_STORAGE. Idąc dalej, zawsze będziesz mieć dostęp do odczytu / zapisu do tych katalogów bez dodatkowych uprawnień.

Aplikacje mogą również kontynuować pracę na starszych urządzeniach, kończąc ich prośbę o pozwolenie w następujący sposób:

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="18" />

Dziękuję Ci. Stworzyłem kod, który jest zgodny z tym, co napisałeś i myślę, że działa dobrze w znajdowaniu ścieżek wszystkich kart SD. Możesz spojrzeć? Tutaj: stackoverflow.com/a/27197248/878126. Przy okazji, nie powinieneś używać "getFreeSpace", ponieważ teoretycznie ilość wolnego miejsca może się zmieniać w trakcie działania.
programista Androida


10

Wykonałem następujące czynności, aby uzyskać dostęp do wszystkich zewnętrznych kart SD.

Z:

File primaryExtSd=Environment.getExternalStorageDirectory();

otrzymasz ścieżkę do głównego zewnętrznego SD Następnie za pomocą:

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

otrzymujesz katalog nadrzędny podstawowej pamięci zewnętrznej, a także jest on nadrzędny dla wszystkich zewnętrznych dysków sd. Teraz możesz wyświetlić listę wszystkich magazynów i wybrać ten, który chcesz.

Mam nadzieję, że jest to przydatne.


To powinna być akceptowana odpowiedź i dziękuję, że jest przydatna.
Meanman,

9

Oto jak otrzymuję listę ścieżek kart SD (z wyłączeniem podstawowej pamięci zewnętrznej):

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

externalCacheDirs[0].getParentFile().getParentFile().getParentFile().getParentFile().getAbsolutePath()To jest przepis na katastrofę. Jeśli to robisz, równie dobrze możesz po prostu zakodować ścieżkę, którą chcesz, ponieważ jeśli otrzymasz katalog pamięci podręcznej z czymkolwiek innym niż 4 rodziców, otrzymasz NullPointerException lub złą ścieżkę.
rharter

@rharter Correct. Naprawiono również ten problem. Proszę spojrzeć.
programista Androida

Dokumentacja ( developer.android.com/reference/android/content/ ... ) zawiera informację „Zwrócone tutaj zewnętrzne urządzenia pamięci masowej są uważane za stałą część urządzenia, w tym zarówno emulowana pamięć zewnętrzna, jak i fizyczne gniazda na nośniki, takie jak karty SD w baterii . Zwracane ścieżki nie obejmują urządzeń przejściowych, takich jak dyski flash USB. " Wygląda na to, że nie mają też slotów na karty SD, czyli miejsc, w których można wyjąć kartę SD bez wchodzenia pod akumulator. Trudno jednak powiedzieć.
LarsH

1
@LarsH Cóż, przetestowałem to sam na SGS3 (i prawdziwej karcie SD), więc myślę, że powinno działać również na innych urządzeniach.
programista Androida

5

Dzięki za wskazówki dostarczone przez was, zwłaszcza @SmartLemon, mam rozwiązanie. Na wypadek, gdyby ktoś tego potrzebował, umieściłem tutaj moje ostateczne rozwiązanie (aby znaleźć pierwszą wymienioną zewnętrzną kartę SD):

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

Jeśli nie ma tam zewnętrznej karty SD, zwraca pamięć na pokładzie. Użyję go, jeśli sdcard nie istnieje, być może będziesz musiał ją zmienić.


1
to zgłoszenie wartości null w systemie Android w wersji 19. rootDir.listFiles () zwraca wartość null. Przetestowałem to z Nexusem 7, emulatorem i Galaxy Note.
Manu Zi

3

zapoznaj się z moim kodem, mam nadzieję, że pomoże ci to:

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

2

W rzeczywistości w niektórych urządzeniach domyślna nazwa zewnętrznej karty SD jest wyświetlana tak, jak w extSdCardprzypadku innych sdcard1.

Ten fragment kodu pomaga znaleźć dokładną ścieżkę i pomaga pobrać ścieżkę do urządzenia zewnętrznego.

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

To bardzo mi pomogło.
Droid Chris

Niektóre urządzenia używają „/ storage / external_sd” (LG G3 KitKat), Marshmallow ma inny schemat, pamięć wewnętrzna na Nexusie 5X z Androidem 6.0 to „/ mnt / sdcard”, a zewnętrzna karta SD znajduje się w „/ storage” / XXXX-XXXX ”
AB

2

Tak. Różni producenci używają innej nazwy karty SD, jak w Samsung Tab 3, jej extsd, a inne urządzenia Samsung używają sdcard, tak jak ten inny producent używa różnych nazw.

Miałem takie same wymagania jak ty. więc utworzyłem dla Ciebie przykładowy przykład z mojego projektu. Przejdź do tego linku Przykład selektora katalogu systemu Android, który używa biblioteki androi-dirchooser. Ten przykład wykrywa kartę SD i wyświetla listę wszystkich podfolderów, a także wykrywa, czy urządzenie ma więcej niż jedną kartę SD.

Część kodu wygląda następująco. Aby uzyskać pełny przykład, przejdź do łącza 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);
        }


    }
});

2

To rozwiązanie (zestawione z innych odpowiedzi na to pytanie) rozwiązuje fakt (jak wspomniał @ono), który System.getenv("SECONDARY_STORAGE")nie jest przydatny w przypadku Marshmallow.

Przetestowane i pracuję nad:

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

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

Warunek rawSecondaryStoragesStr.contains(path)może pozwolić na dodanie pamięci wewnętrznej; możemy lepiej usunąć ten warunek .. Napotkałem ten problem, ponieważ moje urządzenie zwraca wewnętrzny katalog pamięci z System.getenv("SECONDARY_STORAGE"), ale zwraca ścieżkę zewnętrznej karty pamięci z System.getenv(EXTERNAL_STORAGE)na KitKat; wygląda na to, że różni dostawcy zaimplementowali to inaczej, bez żadnego cholernego standardu ..
Gokul NC

Oto moje rozwiązanie, które działa do Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC

1

Na niektórych urządzeniach (na przykład samsung galaxy sII) wewnętrzna karta pamięci może być w formacie vfat. W tym przypadku użyj odwołania do ostatniego kodu, otrzymamy ścieżkę do wewnętrznej karty pamięci (/ mnt / sdcad), ale bez karty zewnętrznej. Kod poniżej rozwiązuje ten problem.

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

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

0

Jestem pewien, że ten kod z pewnością rozwiąże Twoje problemy ... U mnie działa dobrze ... \

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

Environment.getExternalStorageDirectory () nie powinno podawać dokładnej lokalizacji twojej zewnętrznej karty sdcard lub lokalizacji usb. Po prostu przeanalizuj lokalizację "/ proc / mounts", a następnie / dev / fuse / storage / sdcard1 "poda lokalizację, czy twoja karta sd jest poprawnie zamontowany lub nie to samo dla usb, w ten sposób możesz go łatwo zdobyć..Cheers..Sam
Sam

0

to nieprawda. / mnt / sdcard / external_sd może istnieć, nawet jeśli karta SD nie jest zamontowana. Twoja aplikacja ulegnie awarii podczas próby zapisu do / mnt / sdcard / external_sd, gdy nie jest zamontowana.

musisz najpierw sprawdzić, czy karta SD jest zamontowana za pomocą:

boolean isSDPresent = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);

7
getExternalStorageState()Myślę, że jest to bez znaczenia, gdy zwraca się pamięć wewnętrzną, a nie zewnętrzną kartę SD.
Romulus Urakagi Ts'ai

Czy zapewniają teraz funkcję uzyskania zewnętrznej karty SD? Nie mogę znaleźć żadnej wskazówki po przeszukaniu przez godzinę. Jeśli tak jest, to jest to takie dziwne po zaktualizowaniu tylu wersji.
rml

Kiedy powiedziałeś „to nieprawda”, o czym miałeś na myśli? Wiem, to było 2,5 roku temu ...
LarsH

0
 String path = Environment.getExternalStorageDirectory()
                        + File.separator + Environment.DIRECTORY_PICTURES;
                File dir = new File(path);

4
To nie działa. Zwraca ścieżkę do pamięci wewnętrznej. ie / storage /

0

Możesz użyć czegoś takiego jak - Context.getExternalCacheDirs () lub Context.getExternalFilesDirs () lub Context.getObbDirs (). Dają specyficzne dla aplikacji katalogi we wszystkich zewnętrznych urządzeniach pamięci masowej, w których aplikacja może przechowywać swoje pliki.

Coś takiego - Context.getExternalCacheDirs () [i] .getParentFile (). GetParentFile (). GetParentFile (). GetParent () może uzyskać ścieżkę główną zewnętrznych urządzeń pamięci masowej.

Wiem, że te polecenia mają inny cel, ale inne odpowiedzi mi nie pomogły.

Ten link dał mi dobre wskazówki - https://possiblemobile.com/2014/03/android-external-storage/


0

System.getenv("SECONDARY_STORAGE")zwraca wartość null dla Marshmallow. To kolejny sposób na znalezienie wszystkich reżimów zewnętrznych. Możesz sprawdzić, czy jest wymienny, co określa, czy jest wewnętrzny / zewnętrzny

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

0

Wypróbowałem rozwiązania dostarczone przez Dmitriya Lozenko i Gnathonic na moim Samsung Galaxy Tab S2 (model: T819Y), ale żadne nie pomogło mi odzyskać ścieżki do zewnętrznego katalogu na karcie SD. mountwykonanie polecenia zawierało wymaganą ścieżkę do zewnętrznego katalogu karty SD (np. / Storage / A5F9-15F4), ale nie było zgodne z wyrażeniem regularnym, dlatego nie zostało zwrócone. Nie rozumiem mechanizmu nazewnictwa katalogów, po którym jest Samsung. Dlaczego odbiegają od standardów (np. Extsdcard) i wymyślają coś naprawdę podejrzanego, jak w moim przypadku (np. / Storage / A5F9-15F4) . Czy jest coś, czego mi brakuje? Tak czy inaczej, po zmianach w wyrażeniach regularnych Gnathonic rozwiązanie pomogło mi uzyskać prawidłowy katalog sdcard:

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;

Nie jestem pewien, czy to poprawne rozwiązanie i czy da wyniki na innych tabletach Samsunga, ale na razie rozwiązało mój problem. Poniżej znajduje się kolejna metoda odzyskiwania ścieżki wymiennej karty SD w systemie Android (wersja 6.0). Przetestowałem tę metodę na Androidzie marshmallow i działa. Zastosowane w nim podejście jest bardzo podstawowe i na pewno sprawdzi się w innych wersjach, ale testowanie jest obowiązkowe. Pomocny będzie wgląd w to:

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

Prosimy o podzielenie się, jeśli masz inne podejście do rozwiązania tego problemu. Dzięki


Oto moje rozwiązanie, które działa do Nougat: stackoverflow.com/a/40205116/5002496
Gokul NC

Właściwie mam z tobą ten sam problem na Asus Zenfone 2, ale zamiast tego zmieniam kod od Dmitriya Lozenko, aby rozwiązać problem stackoverflow.com/a/40582634/3940133
HendraWD

@HendraWD rozwiązanie, które sugerujesz, może nie działać z Androidem Marshmallow (6.0), ponieważ obsługa zmiennych środowiskowych jest w nim usunięta. Nie pamiętam dokładnie, ale myślę, że spróbowałem.
Abdul Rehman

@GokulNC Spróbuję. wydaje się słuszny :)
Abdul Rehman

@GokulNC tak, dlatego używam context.getExternalFilesDirs (null); zamiast bezpośrednio używać fizycznych ścieżek, gdy wersja jest> Marshmallow
HendraWD

0
String secStore = System.getenv("SECONDARY_STORAGE");

File externalsdpath = new File(secStore);

Spowoduje to uzyskanie ścieżki do zewnętrznej pamięci masowej SD.


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

Jest to alternatywa dla przynajmniej początkujących, aby uzyskać stringList wszystkich sterowników zamontowanych w urządzeniu.
Emre Kilinc Arslan

0

Aby uzyskać dostęp do plików na mojej karcie SD , na moim HTC One X (Android), używam tej ścieżki:

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

Zwróć uwagę na potrójny „/”!


-1

W Galaxy S3 Android 4.3 ścieżka, której używam, to ./storage/extSdCard/Card/ i spełnia swoje zadanie. Mam nadzieję, że to pomoże,


-1

Poniższe kroki zadziałały dla mnie. Musisz tylko napisać te linie:

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

Pierwsza linia poda nazwę katalogu sd i wystarczy jej użyć w metodzie zamiany dla drugiego ciągu. Drugi ciąg będzie zawierał ścieżkę do wewnętrznego i wymiennego sd (/ storage / w moim przypadku) . Potrzebowałem tylko tej ścieżki dla mojej aplikacji, ale możesz przejść dalej, jeśli jej potrzebujesz.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.