Як отримати шлях до зовнішньої SD-карти для Android 4.0+?


94

Samsung Galaxy S3 має зовнішній слот для SD-карти, на який встановлено /mnt/extSdCard.

Як я можу отримати цей шлях чимось на зразок Environment.getExternalStorageDirectory()?

Це повернеться mnt/sdcard, і я не можу знайти API для зовнішньої SD-карти. (Або знімний USB-накопичувач на деяких планшетах.)


Чому ти цього хочеш? На Android дуже не рекомендується робити подібні речі. Можливо, якщо ви поділитесь своєю мотивацією, ми можемо спрямувати вас у напрямку найкращої практики для цього типу речей.
rharter

2
Коли ваш користувач змінює свій телефон, підключіть SD-карту до нового телефону, і на першому телефоні це / sdcard, на другому телефоні / mnt / extSdCard, все, що використовує шлях до файлу, виходить з ладу. Мені потрібно сформувати реальний шлях із його відносного шляху.
Ромул Уракагі Цай

Зараз ця тема стара, але це може допомогти. слід використовувати цей метод. System.getenv (); див. проект Environment3, щоб отримати доступ до всього сховища, підключеного до вашого пристрою. github.com/omidfaraji/Environment3
Омід Фараджі


Ось моє рішення, яке працює до Нуги: stackoverflow.com/a/40205116/5002496
Gokul NC

Відповіді:


58

У мене є варіація рішення, яке я знайшов тут

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

Оригінальний метод був протестований і працював з ним

  • Huawei X3 (в наявності)
  • Galaxy S2 (запас)
  • Galaxy S3 (запас)

Я не впевнений, на якій версії для Android це було, коли їх тестували.

Я протестував свою модифіковану версію за допомогою

  • Moto Xoom 4.1.2 (запас)
  • Galaxy Nexus (cyanogenmod 10) за допомогою кабелю otg
  • HTC Incredible (cyanogenmod 7.2) повернув як внутрішній, так і зовнішній. Цей пристрій є якось дивним, оскільки його внутрішні дані в основному залишаються невикористаними, оскільки getExternalStorage () замість цього повертає шлях до sdcard.

і деякі окремі пристрої зберігання даних, які використовують sdcard як основне сховище

  • HTC G1 (cyanogenmod 6.1)
  • HTC G1 (в наявності)
  • HTC Vision / G2 (акція)

За винятком Неймовірного, всі ці пристрої повернули лише знімне сховище. Ймовірно, є деякі додаткові перевірки, які я повинен робити, але це принаймні трохи краще, ніж будь-яке рішення, яке я знайшов на даний момент.


3
Думаю, у вашому рішенні буде вказано неправильне ім’я папки, якщо оригінальне містить великі літери, такі як / mnt / SdCard
Євген Попович

1
Я тестував на деяких пристроях Motorola, Samsung та LG, і він працював ідеально.
pepedeutico

2
@KhawarRaza, цей метод перестав працювати з kitkat. Скористайтеся методом Дмитра Лозенка, який є запасним варіантом, якщо ви підтримуєте попередні пристрої.
Gnathonic

1
Працює над HUAWEI Honor 3c. Дякую.
li2

2
це повертається / mnt замість / storage на 4.0+ для мене на Galaxy Note 3
оно

54

Я знайшов більш надійний спосіб отримати шляхи до всіх SD-КАРТ в системі. Це працює у всіх версіях Android та зворотних шляхах до всіх сховищ (включаючи емуляцію).

Працює коректно на всіх моїх пристроях.

PS: На основі вихідного коду класу 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
Яким мав бути DIR_SEPORATOR?
cYrixmorten

1
@cYrixmorten його надано поверх коду .. його Patternдля/
Ankit Bansal

Хтось тестував це рішення на Lolipop?
lenrok258

Я спробував цей метод на Lenovo P780, він не працює.
Сатіш Кумар,

2
Працює до льодяника; не працює на Зефір (Galaxy Tab A w / 6.0.1), де System.getenv("SECONDARY_STORAGE")повертається /storage/sdcard1. Це розташування не існує, але фактичне місцезнаходження є /storage/42CE-FD0A, де 42CE-FD0A - серійний номер тома відформатованого розділу.
DaveAlden

32

Я думаю, що для використання зовнішньої sdcard вам потрібно використовувати це:

new File("/mnt/external_sd/")

АБО

new File("/mnt/extSdCard/")

у вашому випадку ...

замість Environment.getExternalStorageDirectory()

Працює для мене. Спершу слід перевірити, що є в каталозі mnt, і працювати звідти ..


Вам слід скористатися певним методом вибору, щоб вибрати, яку sdcard використовувати:

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

1
Ну насправді я хочу метод, а не шлях жорсткого коду, але / mnt / list повинен бути нормальним. Дякую.
Ромул Уракагі Цай

Нема проблем. Є параметр у налаштуваннях, з якого ви обираєте шлях, а потім використовуйте метод, щоб отримати його.
FabianCook

10
На жаль, зовнішня SD-карта може бути в іншій папці, ніж / mnt. Наприклад, на Samsung GT-I9305 це / storage / extSdCard, тоді як вбудована SD-карта має шлях / storage / sdcard0
iseeall

2
Тоді ви отримаєте розташування sdcard, а потім отримаєте його батьківський каталог.
FabianCook

Яким буде шлях зовнішньої SD-карти (для pendrive, підключеного до планшета через кабель OTG) для Android 4.0+?
osimer pothe

15

Для того, щоб отримати всі Зовнішні сховища (будь то карти SD або внутрішні незнімні сховища ), ви можете використовувати такий код:

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

Крім того, ви можете використовувати System.getenv ("EXTERNAL_STORAGE") для отримання первинного каталогу зовнішнього сховища (наприклад, "/ storage / sdcard0" ) і System.getenv ("SECONDARY_STORAGE"), щоб відновити список усіх вторинних каталогів (наприклад, " / storage / extSdCard: / storage / UsbDriveA: / storage / UsbDriveB " ). Пам'ятайте, що також у цьому випадку вам може знадобитися відфільтрувати список вторинних каталогів, щоб виключити USB-накопичувачі.

У будь-якому випадку, будь ласка, зверніть увагу, що використання жорстко закодованих шляхів - це завжди поганий підхід (особливо, коли кожен виробник може змінити його за бажанням).


System.getenv ("SECONDARY_STORAGE") НЕ працював для USB-пристроїв, підключених за допомогою кабелю OTG на Nexus 5 та Nexus 7.
Khawar Raza,

Ось моє рішення, яке працює до Нуги: stackoverflow.com/a/40205116/5002496
Gokul NC

14

Я використовував рішення Дмитра Лозенка , поки не перевірив Asus Zenfone2 , Marshmallow 6.0.1, і рішення не працює. Рішення не вдалося отримати EMULATED_STORAGE_TARGET , особливо для шляху microSD, тобто: / storage / F99C-10F4 / . Я відредагував код, щоб отримати емульовані кореневі шляхи безпосередньо з емульованих шляхів програми context.getExternalFilesDirs(null);та додати більше відомих фізичних шляхів, специфічних для моделі телефону .

Щоб полегшити наше життя, я зробив тут бібліотеку . Ви можете використовувати його через gradle, maven, sbt та leiningen build system.

Якщо вам подобається старомодний спосіб, ви також можете скопіювати файл вставки безпосередньо звідси , але ви не будете знати, чи буде оновлення в майбутньому, не перевіряючи його вручну.

Якщо у вас є питання або пропозиція, будь ласка, повідомте мене


1
Я цим широко не користувався, але це добре працює для моєї проблеми.
Девід

1
Чудове рішення! Однак одне зауваження - якщо SD-карту видалити, коли програма активна, context.getExternalFilesDirs(null)повернеться nullодин із файлів, і в наступному циклі буде виняток. Я пропоную додати if (file == null) continue;як перший рядок у циклі.
Когер

1
@Koger дякую за пропозицію, я додав її та зафіксував друкарську помилку до моєї відповіді
HendraWD

13

Гарні новини! Зараз у KitKat є загальнодоступний API для взаємодії з цими вторинними спільними пристроями зберігання.

Нове Context.getExternalFilesDirs()та Context.getExternalCacheDirs()методи можуть повертати кілька шляхів, включаючи як основні, так і вторинні пристрої. Потім ви можете переглядати їх і перевіряти Environment.getStorageState()та File.getFreeSpace()визначати найкраще місце для зберігання ваших файлів. Ці методи також доступні ContextCompatв бібліотеці support-v4.

Також зауважте, що якщо ви зацікавлені лише у використанні каталогів, які повертає Context, вам більше не потрібні дозволи READ_або WRITE_EXTERNAL_STORAGE. В майбутньому ви завжди матимете доступ до читання / запису до цих каталогів без додаткових дозволів.

Програми також можуть продовжувати працювати на старих пристроях, закінчивши запит на отримання дозволу, як це:

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

Дякую. Я створив код, який відповідає вашому написанню, і, на мою думку, він добре працює при пошуку всіх шляхів на SD-картках. Чи можете ви поглянути? Тут: stackoverflow.com/a/27197248/878126. До речі, ви не повинні використовувати "getFreeSpace", оскільки теоретично вільний простір може змінюватися під час роботи.
розробник android


10

Я зробив наступне, щоб отримати доступ до всіх зовнішніх SD-карт.

З:

File primaryExtSd=Environment.getExternalStorageDirectory();

ви отримуєте шлях до первинного зовнішнього SD Тоді за допомогою:

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

ви отримуєте батьківський каталог основного зовнішнього сховища, а також він є батьківським для всього зовнішнього sd. Тепер ви можете перерахувати весь обсяг пам’яті та вибрати той, який хочете.

Сподіваюся, це корисно.


Це повинна бути прийнята відповідь, і дякую, це корисно.
Meanman

9

Ось як я отримую список шляхів SD-карт (за винятком основного зовнішнього сховища):

  /**
   * 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()Це рецепт катастрофи. Якщо ви робите це, ви можете просто жорстко кодувати шлях, який ви хочете, оскільки, якщо ви отримаєте каталог кешу з чим-небудь, крім 4 батьків, ви отримаєте NullPointerException або неправильний шлях.
rharter

@rharter Правильно. Виправлено і цю проблему. Будь ласка, подивіться.
розробник android

У документації ( developer.android.com/reference/android/content/… ) сказано: "Повернуті сюди зовнішні пристрої зберігання даних вважаються постійною частиною пристрою, включаючи як емульовані зовнішні накопичувачі, так і слоти для фізичних носіїв, такі як SD-картки в акумуляторі Повернуті шляхи не включають перехідні пристрої, такі як флеш-накопичувачі USB. " Здається, вони також не включають слоти для карт SD, тобто місця, де ви можете вийняти SD-карту, не потрапляючи під батарею. Однак важко сказати.
LarsH

1
@LarsH Ну, я сам тестував його на SGS3 (і справжній SD-карті), тому думаю, що він також повинен працювати на інших пристроях.
розробник android

5

Дякую за підказки, надані вами, особливо @SmartLemon, я отримав рішення. Якщо комусь іншому це потрібно, я поміщаю тут своє остаточне рішення (щоб знайти першу перерахувану зовнішню 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;
}

Якщо зовнішньої SD-карти немає, вона повертає вбудовану пам’ять. Я буду використовувати його, якщо sdcard не існує, можливо, вам доведеться її змінити.


1
це викидання null у версії Android 19. rootDir.listFiles () повертає null. Я перевірив це за допомогою nexus 7, емулятора та галактики.
Ману Цзі

3

зверніться до мого коду, сподіваємося для вас корисним:

    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

Насправді на деяких пристроях зовнішнє ім'я SDCard за замовчуванням відображається як extSdCardі для іншихsdcard1 .

Цей фрагмент коду допомагає з’ясувати точний шлях та допоможе отримати шлях зовнішнього пристрою.

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

Це мені надзвичайно допомогло.
Droid Chris

Є деякі пристрої, в яких використовується "/ storage / external_sd" (LG G3 KitKat), Marshmallow має іншу схему, внутрішня пам’ять на Nexus 5X з використанням Android 6.0 має значення "/ mnt / sdcard", а зовнішня SD-карта знаходиться в розділі "/ storage / XXXX-XXXX "
AB

2

Так. Різні виробники використовують різні назви SDcard, як у Samsung Tab 3 extsd, а інші пристрої Samsung використовують sdcard, як у різних виробників різні назви.

У мене була така ж вимога, як і у вас. отже, я створив для вас зразок прикладу з мого проекту, перейшовши за цим посиланням, за допомогою пункту вибору каталогу Android приклад який використовує бібліотеку androi-dirchooser. Цей приклад виявляє SD-карту та перераховує всі вкладені папки, а також виявляє, чи має пристрій більше, ніж одну SD-карту.

Частина коду виглядає так: Для повного прикладу перейдіть за посиланням 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

Це рішення (зібране з інших відповідей на це питання) обробляє той факт (як згадував @ono), який не System.getenv("SECONDARY_STORAGE")має ніякої користі від Marshmallow.

Випробувано та працює над:

  • Samsung Galaxy Tab 2 (Android 4.1.1 - запас)
  • Samsung Galaxy Note 8.0 (Android 4.2.2 - запас)
  • Samsung Galaxy S4 (Android 4.4 - запас)
  • Samsung Galaxy S4 (Android 5.1.1 - Cyanogenmod)
  • Samsung Galaxy Tab A (Android 6.0.1 - запас)

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

Умова rawSecondaryStoragesStr.contains(path)може дозволити внутрішній пам’яті також додаватися; ми можемо краще усунути цю умову .. Я зіткнувся з цією проблемою, оскільки мій пристрій повертає внутрішній каталог пам’яті з System.getenv("SECONDARY_STORAGE"), але повертає шлях зовнішньої картки пам'яті за допомогою System.getenv(EXTERNAL_STORAGE)KitKat; здається, різні постачальники впровадили це по-різному, без жодного проклятого стандарту ..
Gokul NC

Ось моє рішення, яке працює до Нуги: stackoverflow.com/a/40205116/5002496
Gokul NC

1

На деяких пристроях (наприклад, Samsung Galaxy sII) карта внутрішньої пам'яті може бути у vfat. У цьому випадку використовуйте посилання на останній код, ми отримуємо внутрішню карту пам'яті (/ mnt / sdcad), але жодної зовнішньої карти. Код, наведений нижче, вирішує цю проблему.

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

Я впевнений, що цей код, безсумнівно, вирішить ваші проблеми ... Це у мене прекрасно працює ... \

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 () не повинен вказувати точне розташування вашої зовнішньої SD-картки або USB-розташування. Просто проаналізуйте розташування "/ proc / mounts", тоді / dev / fuse / storage / sdcard1 "покаже вам, чи правильно ваша sdcard змонтований чи не однаковий для usb також. Таким чином, ви можете легко його отримати .. Підбадьорення ...
Сам

0

це не правда. / mnt / sdcard / external_sd може існувати, навіть якщо SD-карта не встановлена. ваш додаток вийде з ладу, коли ви спробуєте написати на / mnt / sdcard / external_sd, коли він не змонтований.

Вам потрібно перевірити, чи спочатку встановлена ​​SD-карта, використовуючи:

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

7
getExternalStorageState()Думаю, це безглуздо, коли повертається внутрішня пам’ять, а не зовнішня SD-карта.
Ромул Уракагі Цай

Чи надають вони функцію отримання зовнішньої SD-карти зараз? Я не можу знайти жодної підказки після пошуків протягом години. Якщо це так, то це так дивно після оновлення стільки версій.
rml

Коли ви сказали "це неправда", на що ви мали на увазі? Я знаю, це було 2,5 роки тому ...
Ларш

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

4
Це не працює. Він повертає шлях внутрішнього сховища. ie / storage /

0

Ви можете використовувати щось на зразок - Context.getExternalCacheDirs () або Context.getExternalFilesDirs () або Context.getObbDirs (). Вони дають конкретні каталоги програм на всіх зовнішніх пристроях зберігання, де програма може зберігати свої файли.

Отже, щось подібне - Context.getExternalCacheDirs () [i] .getParentFile (). GetParentFile (). GetParentFile (). GetParent () може отримати вам кореневий шлях зовнішніх пристроїв зберігання.

Я знаю, що ці команди мають іншу мету, але інші відповіді для мене не спрацювали.

Це посилання дало мені хороші вказівки - https://possiblemobile.com/2014/03/android-external-storage/


0

System.getenv("SECONDARY_STORAGE")повертає null для Marshmallow. Це ще один спосіб знайти всі зовнішні папки. Ви можете перевірити, чи він знімний, що визначає внутрішній / зовнішній

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

Я випробував рішення, надані Дмитром Лозенком та Gnathonic на моєму Samsung Galaxy Tab S2 (модель: T819Y), але жодне не допомогло мені отримати шлях до зовнішнього каталогу SD-карт. mountВиконання команди містило необхідний шлях до зовнішнього каталогу SD-картки (тобто / Storage / A5F9-15F4), але воно не відповідало регулярному виразу, тому його не повертали. Я не отримую механізм іменування каталогів, яким керується Samsung. Чому вони відхиляються від стандартів (тобто extsdcard) і придумують щось справді рибне, як у моєму випадку (тобто / Storage / A5F9-15F4) . Щось мені не вистачає? У будь-якому випадку, після змін у регулярному виразі Гнатоніка рішення допомогло мені отримати дійсний каталог 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;

Я не впевнений, чи є це дійсним рішенням і чи воно дасть результати для інших планшетів Samsung, але наразі вирішило мою проблему. Нижче наведено ще один спосіб отримати знімний шлях до SD-карти в Android (v6.0). Я перевірив метод на андроїд зефір, і він працює. Підхід, що використовується в ньому, є дуже простим і, безумовно, буде працювати і для інших версій, але тестування є обов’язковим. Деяке розуміння цього буде корисним:

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

Будь ласка, поділіться, якщо у вас є інший підхід до вирішення цієї проблеми. Дякую


Ось моє рішення, яке працює до Нуги: stackoverflow.com/a/40205116/5002496
Gokul NC

Я на насправді та ж проблема з вами на Asus Zenfone 2, але я змінити код від Дмитро Лозенко замість того, щоб вирішити проблему stackoverflow.com/a/40582634/3940133
HendraWD

@HendraWD рішення, яке ви пропонуєте, може не працювати з Android Marshmallow (6.0), оскільки в ньому видалено підтримку змінних середовища. Я не можу точно згадати, але я думаю, що я спробував.
Абдул Рехман,

@GokulNC Я спробую. здається законним :)
Абдул Рехман,

@GokulNC так, саме тому я використовую context.getExternalFilesDirs (null); замість того, щоб безпосередньо використовувати фізичні шляхи, коли версія> Marshmallow
HendraWD

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

File externalsdpath = new File(secStore);

Це отримає шлях зовнішнього вторинного сховища 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);

Це альтернативно для початківців, принаймні отримання stringList усіх драйверів, встановлених у пристрої.
Емре Кілінц Арслан,

0

Для доступу до файлів на моїй SD-карті на моєму HTC One X (Android) я використовую такий шлях:

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

Зверніть увагу на потрійний "/"!


-1

На Galaxy S3 Android 4.3 шлях, який я використовую, - ./storage/extSdCard/Card/, і він робить свою роботу. Сподіваюся, це допоможе,


-1

Наступні кроки для мене спрацювали. Вам просто потрібно написати ці рядки:

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

Перший рядок дасть назву каталогу sd, і вам просто потрібно використовувати його в методі replace для другого рядка. Другий рядок міститиме шлях до внутрішнього та знімного sd (/ storage / у моєму випадку) . Мені просто знадобився цей шлях для мого додатка, але ви можете піти далі, якщо вам це потрібно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.