Как я могу получить путь к внешней SD-карте для Android 4.0+?

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

Мой вопрос: как пройти этот путь чем-то вроде Environment.getExternalStorageDirectory()? Это вернется mnt/sdcard, и я не могу найти API для внешней SD-карты. (Или съемный USB-накопитель на некоторых планшетах)

Спасибо!

27 ответов

Решение

У меня есть вариант решения, которое я нашел здесь

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 (сток)

За исключением Невероятного все эти устройства возвращали только свои сменные хранилища. Вероятно, есть некоторые дополнительные проверки, которые я должен сделать, но это, по крайней мере, немного лучше, чем любое решение, которое я нашел до сих пор.

Я нашел более надежный способ получить пути ко всем 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()]);
}

Я думаю, чтобы использовать внешнюю 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?
}

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

Чтобы облегчить нашу жизнь, я сделал здесь библиотеку. Вы можете использовать его через систему сборки gradle, maven, sbt и leiningen.

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

Если у вас есть какие-либо вопросы или предложения, пожалуйста, дайте мне знать

Чтобы извлечь все внешние хранилища (будь то карты 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-накопители.

В любом случае, обратите внимание, что использование жестко закодированных путей - это всегда плохой подход (особенно, когда каждый производитель может изменить его по своему усмотрению).

Хорошие новости! В KitKat теперь есть открытый API для взаимодействия с этими вторичными общими устройствами хранения.

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

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

Приложения также могут продолжать работать на старых устройствах, отменив запрос на разрешение, например, так:

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

Я сделал следующее, чтобы получить доступ ко всем внешним SD-карт.

С:

File primaryExtSd=Environment.getExternalStorageDirectory();

Вы получаете путь к первичной внешней SD. Затем с помощью:

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

Вы получаете родительский каталог первичного внешнего хранилища, а также родитель всех внешних внешних дисков. Теперь вы можете перечислить все хранилище и выбрать тот, который вы хотите.

Надеюсь, это полезно.

Вот как я получаю список путей 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)
        return file.getAbsolutePath();
      file=parentFile;
      }
    }

Спасибо за подсказки, предоставленные вами, ребята, особенно @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 не существует, возможно, вам придется изменить его.

Обратитесь к моему коду, надеюсь, полезно для вас:

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

Это решение (собранное из других ответов на этот вопрос) обрабатывает тот факт (как упомянуто @ono), что System.getenv("SECONDARY_STORAGE") бесполезен с Зефиром.

Протестировано и работает над:

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

На самом деле в некоторых устройствах внешнее имя 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);
         }

Да. Разные производители используют разные названия SD-карт, как в 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);
        }


    }
});

На некоторых устройствах (например, 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;
}

System.getenv("SECONDARY_STORAGE") возвращает ноль для Зефира. Это еще один способ найти все внешние каталоги. Вы можете проверить, является ли это съемным, который определяет, является ли внутренний / внешний

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

Я попробовал решения, предоставленные Дмитрием Лозенко и Gnathonic, на моем Samsung Galaxy Tab S2 (модель: T819Y), но ни одно из них не помогло мне найти путь к внешнему каталогу SD-карты. mount Выполнение команды содержало требуемый путь к внешнему каталогу SD-карты (т. е. /Storage/A5F9-15F4), но оно не соответствовало регулярному выражению, поэтому оно не было возвращено. Я не понимаю, как работает механизм именования каталогов от Samsung. Почему они отклоняются от стандартов (например, extsdcard) и придумывают нечто действительно подозрительное, как в моем случае (например, / Storage / A5F9-15F4). Есть что-то, что я пропускаю В любом случае, следующие изменения в регулярном выражении решения Gnathonic помогли мне получить действительный каталог 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). Я проверил метод с зефиром Android, и он работает. Используемый в нем подход очень прост и, безусловно, будет работать и для других версий, но тестирование обязательно. Некоторое понимание этого будет полезно:

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

Пожалуйста, поделитесь, если у вас есть какой-либо другой подход к решению этой проблемы. Спасибо

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

Поэтому что-то вроде этого - Context.getExternalCacheDirs()[i].getParentFile(). GetParentFile (). GetParentFile (). GetParent() может получить корневой путь внешних устройств хранения.

Я знаю, что эти команды предназначены для другой цели, но другие ответы не работали для меня.

Эта ссылка дала мне хорошие советы - https://possiblemobile.com/2014/03/android-external-storage/

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

Это вывод всех надежных методов, работающих на всех версиях Android (надеюсь).

      private static ArrayList<String> getAllStorages(Context context)
{
    ArrayList<String> storagePaths = new ArrayList<>();

    storagePaths.add(Environment.getExternalStorageDirectory().getAbsolutePath());

    try
    {
        File[] externalDirs = context.getExternalFilesDirs(null);
        if (externalDirs != null)
            for (File file : externalDirs)
            {
                storagePaths.add(file.getAbsolutePath());
            }

        externalDirs = context.getExternalMediaDirs();
        if (externalDirs != null)
            for (File file : externalDirs)
            {
                storagePaths.add(file.getAbsolutePath());
            }
    }
    catch (Throwable e)
    {
        // ignore
    }

    try
    {
        StorageManager sm=(StorageManager) context.getSystemService(context.STORAGE_SERVICE);
        for (StorageVolume e:sm.getStorageVolumes())
        {
            String s= (String) StorageVolume.class.getMethod("getPath").invoke(e);
            storagePaths.add(s);
        }
    }
    catch (Throwable e)
    {
        //ignore
    }
    
    for (String key: new String[]{
        "SECONDARY_STORAGE",
        "EXTERNAL_STORAGE",
        "EMULATED_STORAGE_TARGET"
    })
    {
        String externalStorage = System.getenv(key);
        if (externalStorage != null && !externalStorage.isEmpty())
        {
            String[] externalPaths = externalStorage.split(":");
            for (String e:externalPaths)
            {
                storagePaths.add(e);
            }
        }
    }

    ArrayList<String> result=new ArrayList<>();
    for (String s:storagePaths)
    {
        File f=new File(s);
        File f2=f;
        while (f2 != null)
        {
            if (f2.canRead())
            {
                f = f2;
            }
            f2 = f2.getParentFile();
        }
        try
        {
            f = f.getCanonicalFile();
        }
        catch (IOException e)
        {
            // ignore
        }
        s = f.getPath();
        if (!result.contains(s))
        {
            result.add(s);
        }
    }
    return result;
}

Протестировано на Android 11.

Вам следует протестировать его на каждой версии и оставить комментарий ниже, если какая-либо из них несовместима, чтобы вы могли ее улучшить.

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

File externalsdpath = new File(secStore);

Это позволит получить путь к внешнему SD-хранилищу.

Для доступа к файлам на моей SD-карте в HTC One X (Android) я использую этот путь:

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

Обратите внимание на тройку "/"!

Я уверен, что этот код наверняка решит ваши проблемы... Это нормально работает для меня...\

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

Это не правда. /mnt/sdcard/external_sd может существовать, даже если SD-карта не установлена. ваше приложение будет зависать при попытке записи в / mnt / sdcard / external_sd, когда оно не смонтировано.

Вы должны проверить, установлена ​​ли SD-карта сначала, используя:

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

Следующие шаги работали для меня. Вам просто нужно написать следующие строки:

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

В первой строке будет указано имя каталога sd, и вам просто нужно использовать его в методе замены для второй строки. Вторая строка будет содержать путь для внутреннего и сменного SD (в моем случае /storage/). Мне просто нужен этот путь для моего приложения, но вы можете пойти дальше, если вам это нужно.

На Galaxy S3 Android 4.3 я использую путь ./storage/extSdCard/Card/, и он делает свою работу. Надеюсь, поможет,

Другие вопросы по тегам