Find location of a removable SD card

前端 未结 22 2475
南方客
南方客 2020-11-21 11:32

Is there an universal way to find the location of an external SD card?

Please, do not be confused with External Storage.

Environment.getExternalStorage

相关标签:
22条回答
  • 2020-11-21 11:52

    I came up with the following solution based on some answers found here.

    CODE:

    public class ExternalStorage {
    
        public static final String SD_CARD = "sdCard";
        public static final String EXTERNAL_SD_CARD = "externalSdCard";
    
        /**
         * @return True if the external storage is available. False otherwise.
         */
        public static boolean isAvailable() {
            String state = Environment.getExternalStorageState();
            if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
                return true;
            }
            return false;
        }
    
        public static String getSdCardPath() {
            return Environment.getExternalStorageDirectory().getPath() + "/";
        }
    
        /**
         * @return True if the external storage is writable. False otherwise.
         */
        public static boolean isWritable() {
            String state = Environment.getExternalStorageState();
            if (Environment.MEDIA_MOUNTED.equals(state)) {
                return true;
            }
            return false;
    
        }
    
        /**
         * @return A map of all storage locations available
         */
        public static Map<String, File> getAllStorageLocations() {
            Map<String, File> map = new HashMap<String, File>(10);
    
            List<String> mMounts = new ArrayList<String>(10);
            List<String> mVold = new ArrayList<String>(10);
            mMounts.add("/mnt/sdcard");
            mVold.add("/mnt/sdcard");
    
            try {
                File mountFile = new File("/proc/mounts");
                if(mountFile.exists()){
                    Scanner scanner = new Scanner(mountFile);
                    while (scanner.hasNext()) {
                        String line = scanner.nextLine();
                        if (line.startsWith("/dev/block/vold/")) {
                            String[] lineElements = line.split(" ");
                            String element = lineElements[1];
    
                            // don't add the default mount path
                            // it's already in the list.
                            if (!element.equals("/mnt/sdcard"))
                                mMounts.add(element);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            try {
                File voldFile = new File("/system/etc/vold.fstab");
                if(voldFile.exists()){
                    Scanner scanner = new Scanner(voldFile);
                    while (scanner.hasNext()) {
                        String line = scanner.nextLine();
                        if (line.startsWith("dev_mount")) {
                            String[] lineElements = line.split(" ");
                            String element = lineElements[2];
    
                            if (element.contains(":"))
                                element = element.substring(0, element.indexOf(":"));
                            if (!element.equals("/mnt/sdcard"))
                                mVold.add(element);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
    
    
            for (int i = 0; i < mMounts.size(); i++) {
                String mount = mMounts.get(i);
                if (!mVold.contains(mount))
                    mMounts.remove(i--);
            }
            mVold.clear();
    
            List<String> mountHash = new ArrayList<String>(10);
    
            for(String mount : mMounts){
                File root = new File(mount);
                if (root.exists() && root.isDirectory() && root.canWrite()) {
                    File[] list = root.listFiles();
                    String hash = "[";
                    if(list!=null){
                        for(File f : list){
                            hash += f.getName().hashCode()+":"+f.length()+", ";
                        }
                    }
                    hash += "]";
                    if(!mountHash.contains(hash)){
                        String key = SD_CARD + "_" + map.size();
                        if (map.size() == 0) {
                            key = SD_CARD;
                        } else if (map.size() == 1) {
                            key = EXTERNAL_SD_CARD;
                        }
                        mountHash.add(hash);
                        map.put(key, root);
                    }
                }
            }
    
            mMounts.clear();
    
            if(map.isEmpty()){
                     map.put(SD_CARD, Environment.getExternalStorageDirectory());
            }
            return map;
        }
    }
    

    USAGE:

    Map<String, File> externalLocations = ExternalStorage.getAllStorageLocations();
    File sdCard = externalLocations.get(ExternalStorage.SD_CARD);
    File externalSdCard = externalLocations.get(ExternalStorage.EXTERNAL_SD_CARD);
    
    0 讨论(0)
  • 2020-11-21 11:56

    I try all solutions inside this topic on this time. But all of them did not work correctly on devices with one external (removable) and one internal (not-removable) cards. Path of external card not possible get from 'mount' command, from 'proc/mounts' file etc.

    And I create my own solution (on Paulo Luan's):

    String sSDpath = null;
    File   fileCur = null;
    for( String sPathCur : Arrays.asList( "ext_card", "external_sd", "ext_sd", "external", "extSdCard",  "externalSdCard")) // external sdcard
    {
       fileCur = new File( "/mnt/", sPathCur);
       if( fileCur.isDirectory() && fileCur.canWrite())
       {
         sSDpath = fileCur.getAbsolutePath();
         break;
       }
    }
    fileCur = null;
    if( sSDpath == null)  sSDpath = Environment.getExternalStorageDirectory().getAbsolutePath();
    
    0 讨论(0)
  • 2020-11-21 11:57

    I have created a utils method to check a SD card is available on device or not, and get SD card path on device if it available.

    You can copy 2 methods bellow into your project's class that you need. That's all.

    public String isRemovableSDCardAvailable() {
        final String FLAG = "mnt";
        final String SECONDARY_STORAGE = System.getenv("SECONDARY_STORAGE");
        final String EXTERNAL_STORAGE_DOCOMO = System.getenv("EXTERNAL_STORAGE_DOCOMO");
        final String EXTERNAL_SDCARD_STORAGE = System.getenv("EXTERNAL_SDCARD_STORAGE");
        final String EXTERNAL_SD_STORAGE = System.getenv("EXTERNAL_SD_STORAGE");
        final String EXTERNAL_STORAGE = System.getenv("EXTERNAL_STORAGE");
    
        Map<Integer, String> listEnvironmentVariableStoreSDCardRootDirectory = new HashMap<Integer, String>();
        listEnvironmentVariableStoreSDCardRootDirectory.put(0, SECONDARY_STORAGE);
        listEnvironmentVariableStoreSDCardRootDirectory.put(1, EXTERNAL_STORAGE_DOCOMO);
        listEnvironmentVariableStoreSDCardRootDirectory.put(2, EXTERNAL_SDCARD_STORAGE);
        listEnvironmentVariableStoreSDCardRootDirectory.put(3, EXTERNAL_SD_STORAGE);
        listEnvironmentVariableStoreSDCardRootDirectory.put(4, EXTERNAL_STORAGE);
    
        File externalStorageList[] = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
            externalStorageList = getContext().getExternalFilesDirs(null);
        }
        String directory = null;
        int size = listEnvironmentVariableStoreSDCardRootDirectory.size();
        for (int i = 0; i < size; i++) {
            if (externalStorageList != null && externalStorageList.length > 1 && externalStorageList[1] != null)
                directory = externalStorageList[1].getAbsolutePath();
            else
                directory = listEnvironmentVariableStoreSDCardRootDirectory.get(i);
    
            directory = canCreateFile(directory);
            if (directory != null && directory.length() != 0) {
                if (i == size - 1) {
                    if (directory.contains(FLAG)) {
                        Log.e(getClass().getSimpleName(), "SD Card's directory: " + directory);
                        return directory;
                    } else {
                        return null;
                    }
                }
                Log.e(getClass().getSimpleName(), "SD Card's directory: " + directory);
                return directory;
            }
        }
        return null;
    }
    
    /**
     * Check if can create file on given directory. Use this enclose with method
     * {@link BeginScreenFragement#isRemovableSDCardAvailable()} to check sd
     * card is available on device or not.
     * 
     * @param directory
     * @return
     */
    public String canCreateFile(String directory) {
        final String FILE_DIR = directory + File.separator + "hoang.txt";
        File tempFlie = null;
        try {
            tempFlie = new File(FILE_DIR);
            FileOutputStream fos = new FileOutputStream(tempFlie);
            fos.write(new byte[1024]);
            fos.flush();
            fos.close();
            Log.e(getClass().getSimpleName(), "Can write file on this directory: " + FILE_DIR);
        } catch (Exception e) {
            Log.e(getClass().getSimpleName(), "Write file error: " + e.getMessage());
            return null;
        } finally {
            if (tempFlie != null && tempFlie.exists() && tempFlie.isFile()) {
                // tempFlie.delete();
                tempFlie = null;
            }
        }
        return directory;
    }
    
    0 讨论(0)
  • 2020-11-21 11:57

    By writing below code you will get the location:

    /storage/663D-554E/Android/data/app_package_name/files/

    which stores your app data at /android/data location inside the sd_card.

    File[] list = ContextCompat.getExternalFilesDirs(MainActivity.this, null);
    
    list[1]+"/fol" 
    

    for getting location pass 0 for internal and 1 for sdcard to file array.

    I have tested this code on a moto g4 plus and Samsung device (all works fine).

    hope this might helpful.

    0 讨论(0)
  • 2020-11-21 11:57

    Its work for all external devices, But make sure only get external device folder name and then you need to get file from given location using File class.

    public static List<String> getExternalMounts() {
            final List<String> out = new ArrayList<>();
            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;
        }
    

    Calling:

    List<String> list=getExternalMounts();
            if(list.size()>0)
            {
                String[] arr=list.get(0).split("/");
                int size=0;
                if(arr!=null && arr.length>0) {
                    size= arr.length - 1;
                }
                File parentDir=new File("/storage/"+arr[size]);
                if(parentDir.listFiles()!=null){
                    File parent[] = parentDir.listFiles();
    
                    for (int i = 0; i < parent.length; i++) {
    
                        // get file path as parent[i].getAbsolutePath());
    
                    }
                }
            }
    

    Getting access to external storage

    In order to read or write files on the external storage, your app must acquire the READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE system permissions. For example:

    <manifest ...>
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
        ...
    </manifest>
    
    0 讨论(0)
  • 2020-11-21 11:58

    Environment.getExternalStorageState() returns path to internal SD mount point like "/mnt/sdcard"

    No, Environment.getExternalStorageDirectory() refers to whatever the device manufacturer considered to be "external storage". On some devices, this is removable media, like an SD card. On some devices, this is a portion of on-device flash. Here, "external storage" means "the stuff accessible via USB Mass Storage mode when mounted on a host machine", at least for Android 1.x and 2.x.

    But the question is about external SD. How to get a path like "/mnt/sdcard/external_sd" (it may differ from device to device)?

    Android has no concept of "external SD", aside from external storage, as described above.

    If a device manufacturer has elected to have external storage be on-board flash and also has an SD card, you will need to contact that manufacturer to determine whether or not you can use the SD card (not guaranteed) and what the rules are for using it, such as what path to use for it.


    UPDATE

    Two recent things of note:

    First, on Android 4.4+, you do not have write access to removable media (e.g., "external SD"), except for any locations on that media that might be returned by getExternalFilesDirs() and getExternalCacheDirs(). See Dave Smith's excellent analysis of this, particularly if you want the low-level details.

    Second, lest anyone quibble on whether or not removable media access is otherwise part of the Android SDK, here is Dianne Hackborn's assessment:

    ...keep in mind: until Android 4.4, the official Android platform has not supported SD cards at all except for two special cases: the old school storage layout where external storage is an SD card (which is still supported by the platform today), and a small feature added to Android 3.0 where it would scan additional SD cards and add them to the media provider and give apps read-only access to their files (which is also still supported in the platform today).

    Android 4.4 is the first release of the platform that has actually allowed applications to use SD cards for storage. Any access to them prior to that was through private, unsupported APIs. We now have a quite rich API in the platform that allows applications to make use of SD cards in a supported way, in better ways than they have been able to before: they can make free use of their app-specific storage area without requiring any permissions in the app, and can access any other files on the SD card as long as they go through the file picker, again without needing any special permissions.

    0 讨论(0)
提交回复
热议问题