Android Open External Storage directory(sdcard) for storing file
Asked Answered
F

8

37

I want to open external storage directory path for saving file programatically.I tried but not getting sdcard path. How can i do this?is there any solution for this??

private File path = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "");

or

private File path = new File(Environment.getExternalStorageDirectory() + "");

I tried getting path from above both methods but both are pointing internal memory.

When we open storage memory if sdcard is peresent it will shows like below- enter image description here

device storage & sd memory card.

I want to get sd memory path through coding. I have given permissions in manifest-

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Footton answered 6/3, 2014 at 8:47 Comment(7)
yes.I have Given WRITE_EXTERNAL STORAGE permissionDeterrence
Also Add READ_EXTERNAL STORAGE permissionMaidenhair
@BlackTiger : still not getting sdcard pathDeterrence
String root = Environment.getExternalStorageDirectory().toString(); It you have done everything correct, It will return the path as well. Please post your complete codeFullscale
The path your have given is same as what i tried.I have posted it.I want sdcard directory path.n what you are saying gives internal memory/device memoryDeterrence
@yuvaツ use this as i mentioned in my answer: String pathName = "/mnt/"; or use this: String pathName = "/storage/";Stonefish
Use my solution #69150934Fructify
D
61

I had been having the exact same problem!

To get the internal SD card you can use

String extStore = System.getenv("EXTERNAL_STORAGE");
File f_exts = new File(extStore);

To get the external SD card you can use

String secStore = System.getenv("SECONDARY_STORAGE");
File f_secs = new File(secStore);

On running the code

 extStore = "/storage/emulated/legacy"
 secStore = "/storage/extSdCarcd"

works perfectly!

Dugger answered 30/5, 2014 at 7:53 Comment(8)
it is good to differentiate between primary external and secondary external storage also.. thanks...Edisonedit
Thanks for the answer. I added a complement to it, but it didn't fit here in the comments. Set your name on my answer as the original answer.Oysterman
@neerajkirola visit https://mcmap.net/q/162649/-difference-between-getexternalfilesdir-and-getexternalstoragedirectory for difference between primary external and secondary external storageBreathtaking
System.getenv("SECONDARY_STORAGE") is returning null when i tried in android emulatorNewsstand
@bikrampandit : that's most probably because you don't have a "secondary" storage device connected with the emulator. Also, I have noticed that the new versions of Android have combined the two memories together for some applications, I doubt it'll be that though.Dugger
Doesn't works on Galaxy 7 Edge with Android 7. System.getenv("SECONDARY_STORAGE") returns null. but SDCard is inserted.Antalya
@BikramPandit "SECONDARY_STORAGE" may have another string name in different devicesLeslee
System.getenv("SECONDARY_STORAGE") returns null on my phone that has SD card. Any idea why Google have made this so needlessly complex?Catalpa
C
20

The internal storage is referred to as "external storage" in the API.

As mentioned in the Environment documentation

Note: don't be confused by the word "external" here. This directory can better be thought as media/shared storage. It is a filesystem that can hold a relatively large amount of data and that is shared across all applications (does not enforce permissions). Traditionally this is an SD card, but it may also be implemented as built-in storage in a device that is distinct from the protected internal storage and can be mounted as a filesystem on a computer.

To distinguish whether "Environment.getExternalStorageDirectory()" actually returned physically internal or external storage, call Environment.isExternalStorageEmulated(). If it's emulated, than it's internal. On newer devices that have internal storage and sdcard slot Environment.getExternalStorageDirectory() will always return the internal storage. While on older devices that have only sdcard as a media storage option it will always return the sdcard.

There is no way to retrieve all storages using current Android API.

I've created a helper based on Vitaliy Polchuk's method in the answer below

How can I get the list of mounted external storage of android device

NOTE: starting KitKat secondary storage is accessible only as READ-ONLY, you may want to check for writability using the following method

/**
 * Checks whether the StorageVolume is read-only
 * 
 * @param volume
 *            StorageVolume to check
 * @return true, if volume is mounted read-only
 */
public static boolean isReadOnly(@NonNull final StorageVolume volume) {
    if (volume.mFile.equals(Environment.getExternalStorageDirectory())) {
        // is a primary storage, check mounted state by Environment
        return android.os.Environment.getExternalStorageState().equals(
                android.os.Environment.MEDIA_MOUNTED_READ_ONLY);
    } else {
        if (volume.getType() == Type.USB) {
            return volume.isReadOnly();
        }
        //is not a USB storagem so it's read-only if it's mounted read-only or if it's a KitKat device
        return volume.isReadOnly() || Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
    }
}

StorageHelper class

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.StringTokenizer;

import android.os.Environment;

public final class StorageHelper {

    //private static final String TAG = "StorageHelper";

    private StorageHelper() {
    }

    private static final String STORAGES_ROOT;

    static {
        final String primaryStoragePath = Environment.getExternalStorageDirectory()
                .getAbsolutePath();
        final int index = primaryStoragePath.indexOf(File.separatorChar, 1);
        if (index != -1) {
            STORAGES_ROOT = primaryStoragePath.substring(0, index + 1);
        } else {
            STORAGES_ROOT = File.separator;
        }
    }

    private static final String[] AVOIDED_DEVICES = new String[] {
        "rootfs", "tmpfs", "dvpts", "proc", "sysfs", "none"
    };

    private static final String[] AVOIDED_DIRECTORIES = new String[] {
        "obb", "asec"
    };

    private static final String[] DISALLOWED_FILESYSTEMS = new String[] {
        "tmpfs", "rootfs", "romfs", "devpts", "sysfs", "proc", "cgroup", "debugfs"
    };

    /**
     * Returns a list of mounted {@link StorageVolume}s Returned list always
     * includes a {@link StorageVolume} for
     * {@link Environment#getExternalStorageDirectory()}
     * 
     * @param includeUsb
     *            if true, will include USB storages
     * @return list of mounted {@link StorageVolume}s
     */
    public static List<StorageVolume> getStorages(final boolean includeUsb) {
        final Map<String, List<StorageVolume>> deviceVolumeMap = new HashMap<String, List<StorageVolume>>();

        // this approach considers that all storages are mounted in the same non-root directory
        if (!STORAGES_ROOT.equals(File.separator)) {
            BufferedReader reader = null;
            try {
                reader = new BufferedReader(new FileReader("/proc/mounts"));
                String line;
                while ((line = reader.readLine()) != null) {
                    // Log.d(TAG, line);
                    final StringTokenizer tokens = new StringTokenizer(line, " ");

                    final String device = tokens.nextToken();
                    // skipped devices that are not sdcard for sure
                    if (arrayContains(AVOIDED_DEVICES, device)) {
                        continue;
                    }

                    // should be mounted in the same directory to which
                    // the primary external storage was mounted
                    final String path = tokens.nextToken();
                    if (!path.startsWith(STORAGES_ROOT)) {
                        continue;
                    }

                    // skip directories that indicate tha volume is not a storage volume
                    if (pathContainsDir(path, AVOIDED_DIRECTORIES)) {
                        continue;
                    }

                    final String fileSystem = tokens.nextToken();
                    // don't add ones with non-supported filesystems
                    if (arrayContains(DISALLOWED_FILESYSTEMS, fileSystem)) {
                        continue;
                    }

                    final File file = new File(path);
                    // skip volumes that are not accessible
                    if (!file.canRead() || !file.canExecute()) {
                        continue;
                    }

                    List<StorageVolume> volumes = deviceVolumeMap.get(device);
                    if (volumes == null) {
                        volumes = new ArrayList<StorageVolume>(3);
                        deviceVolumeMap.put(device, volumes);
                    }

                    final StorageVolume volume = new StorageVolume(device, file, fileSystem);
                    final StringTokenizer flags = new StringTokenizer(tokens.nextToken(), ",");
                    while (flags.hasMoreTokens()) {
                        final String token = flags.nextToken();
                        if (token.equals("rw")) {
                            volume.mReadOnly = false;
                            break;
                        } else if (token.equals("ro")) {
                            volume.mReadOnly = true;
                            break;
                        }
                    }
                    volumes.add(volume);
                }

            } catch (IOException ex) {
                ex.printStackTrace();
            } finally {
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException ex) {
                        // ignored
                    }
                }
            }
        }

        // remove volumes that are the same devices
        boolean primaryStorageIncluded = false;
        final File externalStorage = Environment.getExternalStorageDirectory();
        final List<StorageVolume> volumeList = new ArrayList<StorageVolume>();
        for (final Entry<String, List<StorageVolume>> entry : deviceVolumeMap.entrySet()) {
            final List<StorageVolume> volumes = entry.getValue();
            if (volumes.size() == 1) {
                // go ahead and add
                final StorageVolume v = volumes.get(0);
                final boolean isPrimaryStorage = v.file.equals(externalStorage);
                primaryStorageIncluded |= isPrimaryStorage;
                setTypeAndAdd(volumeList, v, includeUsb, isPrimaryStorage);
                continue;
            }
            final int volumesLength = volumes.size();
            for (int i = 0; i < volumesLength; i++) {
                final StorageVolume v = volumes.get(i);
                if (v.file.equals(externalStorage)) {
                    primaryStorageIncluded = true;
                    // add as external storage and continue
                    setTypeAndAdd(volumeList, v, includeUsb, true);
                    break;
                }
                // if that was the last one and it's not the default external
                // storage then add it as is
                if (i == volumesLength - 1) {
                    setTypeAndAdd(volumeList, v, includeUsb, false);
                }
            }
        }
        // add primary storage if it was not found
        if (!primaryStorageIncluded) {
            final StorageVolume defaultExternalStorage = new StorageVolume("", externalStorage, "UNKNOWN");
            defaultExternalStorage.mEmulated = Environment.isExternalStorageEmulated();
            defaultExternalStorage.mType =
                    defaultExternalStorage.mEmulated ? StorageVolume.Type.INTERNAL
                            : StorageVolume.Type.EXTERNAL;
            defaultExternalStorage.mRemovable = Environment.isExternalStorageRemovable();
            defaultExternalStorage.mReadOnly =
                    Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY);
            volumeList.add(0, defaultExternalStorage);
        }
        return volumeList;
    }

    /**
     * Sets {@link StorageVolume.Type}, removable and emulated flags and adds to
     * volumeList
     * 
     * @param volumeList
     *            List to add volume to
     * @param v
     *            volume to add to list
     * @param includeUsb
     *            if false, volume with type {@link StorageVolume.Type#USB} will
     *            not be added
     * @param asFirstItem
     *            if true, adds the volume at the beginning of the volumeList
     */
    private static void setTypeAndAdd(final List<StorageVolume> volumeList,
            final StorageVolume v,
            final boolean includeUsb,
            final boolean asFirstItem) {
        final StorageVolume.Type type = resolveType(v);
        if (includeUsb || type != StorageVolume.Type.USB) {
            v.mType = type;
            if (v.file.equals(Environment.getExternalStorageDirectory())) {
                v.mRemovable = Environment.isExternalStorageRemovable();
            } else {
                v.mRemovable = type != StorageVolume.Type.INTERNAL;
            }
            v.mEmulated = type == StorageVolume.Type.INTERNAL;
            if (asFirstItem) {
                volumeList.add(0, v);
            } else {
                volumeList.add(v);
            }
        }
    }

    /**
     * Resolved {@link StorageVolume} type
     * 
     * @param v
     *            {@link StorageVolume} to resolve type for
     * @return {@link StorageVolume} type
     */
    private static StorageVolume.Type resolveType(final StorageVolume v) {
        if (v.file.equals(Environment.getExternalStorageDirectory())
                && Environment.isExternalStorageEmulated()) {
            return StorageVolume.Type.INTERNAL;
        } else if (containsIgnoreCase(v.file.getAbsolutePath(), "usb")) {
            return StorageVolume.Type.USB;
        } else {
            return StorageVolume.Type.EXTERNAL;
        }
    }

    /**
     * Checks whether the array contains object
     * 
     * @param array
     *            Array to check
     * @param object
     *            Object to find
     * @return true, if the given array contains the object
     */
    private static <T> boolean arrayContains(T[] array, T object) {
        for (final T item : array) {
            if (item.equals(object)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Checks whether the path contains one of the directories
     * 
     * For example, if path is /one/two, it returns true input is "one" or
     * "two". Will return false if the input is one of "one/two", "/one" or
     * "/two"
     * 
     * @param path
     *            path to check for a directory
     * @param dirs
     *            directories to find
     * @return true, if the path contains one of the directories
     */
    private static boolean pathContainsDir(final String path, final String[] dirs) {
        final StringTokenizer tokens = new StringTokenizer(path, File.separator);
        while (tokens.hasMoreElements()) {
            final String next = tokens.nextToken();
            for (final String dir : dirs) {
                if (next.equals(dir)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Checks ifString contains a search String irrespective of case, handling.
     * Case-insensitivity is defined as by
     * {@link String#equalsIgnoreCase(String)}.
     * 
     * @param str
     *            the String to check, may be null
     * @param searchStr
     *            the String to find, may be null
     * @return true if the String contains the search String irrespective of
     *         case or false if not or {@code null} string input
     */
    public static boolean containsIgnoreCase(final String str, final String searchStr) {
        if (str == null || searchStr == null) {
            return false;
        }
        final int len = searchStr.length();
        final int max = str.length() - len;
        for (int i = 0; i <= max; i++) {
            if (str.regionMatches(true, i, searchStr, 0, len)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Represents storage volume information
     */
    public static final class StorageVolume {

        /**
         * Represents {@link StorageVolume} type
         */
        public enum Type {
            /**
             * Device built-in internal storage. Probably points to
             * {@link Environment#getExternalStorageDirectory()}
             */
            INTERNAL,

            /**
             * External storage. Probably removable, if no other
             * {@link StorageVolume} of type {@link #INTERNAL} is returned by
             * {@link StorageHelper#getStorages(boolean)}, this might be
             * pointing to {@link Environment#getExternalStorageDirectory()}
             */
            EXTERNAL,

            /**
             * Removable usb storage
             */
            USB
        }

        /**
         * Device name
         */
        public final String device;

        /**
         * Points to mount point of this device
         */
        public final File file;

        /**
         * File system of this device
         */
        public final String fileSystem;

        /**
         * if true, the storage is mounted as read-only
         */
        private boolean mReadOnly;

        /**
         * If true, the storage is removable
         */
        private boolean mRemovable;

        /**
         * If true, the storage is emulated
         */
        private boolean mEmulated;

        /**
         * Type of this storage
         */
        private Type mType;

        StorageVolume(String device, File file, String fileSystem) {
            this.device = device;
            this.file = file;
            this.fileSystem = fileSystem;
        }

        /**
         * Returns type of this storage
         * 
         * @return Type of this storage
         */
        public Type getType() {
            return mType;
        }

        /**
         * Returns true if this storage is removable
         * 
         * @return true if this storage is removable
         */
        public boolean isRemovable() {
            return mRemovable;
        }

        /**
         * Returns true if this storage is emulated
         * 
         * @return true if this storage is emulated
         */
        public boolean isEmulated() {
            return mEmulated;
        }

        /**
         * Returns true if this storage is mounted as read-only
         * 
         * @return true if this storage is mounted as read-only
         */
        public boolean isReadOnly() {
            return mReadOnly;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((file == null) ? 0 : file.hashCode());
            return result;
        }

        /**
         * Returns true if the other object is StorageHelper and it's
         * {@link #file} matches this one's
         * 
         * @see Object#equals(Object)
         */
        @Override
        public boolean equals(Object obj) {
            if (obj == this) {
                return true;
            }
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final StorageVolume other = (StorageVolume) obj;
            if (file == null) {
                return other.file == null;
            }
            return file.equals(other.file);
        }

        @Override
        public String toString() {
            return file.getAbsolutePath() + (mReadOnly ? " ro " : " rw ") + mType + (mRemovable ? " R " : "")
                    + (mEmulated ? " E " : "") + fileSystem;
        }
    }
}
Cindicindie answered 6/3, 2014 at 10:22 Comment(12)
Is it still returning null if you remove StringUtils.containsIgnoreCase() and just return first found read-write-execute one?Cindicindie
If i remove if condition returning device directory pathDeterrence
I mean don't remove the first if but the StringUtils-one if. Can you give me an output of "adb shell ls /storage"?Cindicindie
/storage/emulated/legacyDeterrence
My approach sure worked on all devices I've tried, but I guess it's not the best way then. A good thing I figured that out because I was going to use that in production. What is the real location of sdcard found in third-party applications in your device (and device name please)? My approach works if the external one is on /storage/emulated/ dir. When does "adb shell ls /storage/emulated/" prints?Cindicindie
You can try this answer then https://mcmap.net/q/162650/-how-can-i-get-the-list-of-mounted-external-storage-of-android-device I don't really like it, I would've changed phew points there like allowing "ntfs" besides vfat etc but generally reading mounts would be more reliable.Cindicindie
let us continue this discussion in chatDeterrence
sorry my output using adb shell is shell@android:/storage $ ls UsbDriveA UsbDriveB UsbDriveC UsbDriveD UsbDriveE UsbDriveF emulated extSdCard sdcard0Deterrence
i tried as per link you have gieven.still not gettingDeterrence
final List<StorageVolume> storages = getStorages(false); then determine which is an actual SDCard by storageVolume.isRemovable(). and get the directory by storageVolume.fileCindicindie
there is no any method in above class getStorage();Deterrence
What do you mean there isn't? I can clearly see StorageHelper.getStorages(boolean) method.Cindicindie
T
4

taking @rijul's answer forward, it doesn't work in marshmallow and above versions:

       //for pre-marshmallow versions
       String path = System.getenv("SECONDARY_STORAGE");

       // For Marshmallow, use getExternalCacheDirs() instead of System.getenv("SECONDARY_STORAGE")
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
           File[] externalCacheDirs = mContext.getExternalCacheDirs();
           for (File file : externalCacheDirs) {
               if (Environment.isExternalStorageRemovable(file)) {
                   // Path is in format /storage.../Android....
                   // Get everything before /Android
                   path = file.getPath().split("/Android")[0];
                   break;
               }
           }
       }


        // Android avd emulator doesn't support this variable name so using other one
        if ((null == path) || (path.length() == 0))
            path = Environment.getExternalStorageDirectory().getAbsolutePath();
Tillfourd answered 29/6, 2017 at 5:58 Comment(0)
S
3

hope it's worked for you:

File yourFile = new File(Environment.getExternalStorageDirectory(), "textarabics.txt");

This will give u sdcard path:

File path = Environment.getExternalStorageDirectory();

Try this:

String pathName = "/mnt/";

or try this:

String pathName = "/storage/";
Stonefish answered 6/3, 2014 at 10:16 Comment(4)
It's device dependent.different for different devicesDeterrence
@FarhanShah first two options don't show anything new for the OP. Second two (/mnt or /storage) can be mount points but not the actual external storage and plus the mount point vary anyway, so the answer is not useful with two first statements and misleading with second two.Cindicindie
@DoctororDrive i try my best and sincerely answered to the OP,so there is no reason for downvoting..Stonefish
@FarhanShah the answer can be downvoted if it does not answer the question or is wrong. In my opinion, no matter if you tried your best or not - it's a bad answer. People make mistakes and the vote system is created to measure answer usefulness.Cindicindie
O
2

Complementing rijul gupta answer:

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

    if ((strSDCardPath == null) || (strSDCardPath.length() == 0)) {
        strSDCardPath = System.getenv("EXTERNAL_SDCARD_STORAGE");
    }

    //If may get a full path that is not the right one, even if we don't have the SD Card there. 
    //We just need the "/mnt/extSdCard/" i.e and check if it's writable
    if(strSDCardPath != null) {
        if (strSDCardPath.contains(":")) {
            strSDCardPath = strSDCardPath.substring(0, strSDCardPath.indexOf(":"));
        }
        File externalFilePath = new File(strSDCardPath);

        if (externalFilePath.exists() && externalFilePath.canWrite()){
            //do what you need here
        }
    }
Oysterman answered 1/6, 2015 at 14:36 Comment(0)
B
0

I want to open external storage directory path for saving file programatically.I tried but not getting sdcard path. How can i do this?is there any solution for this??

To store your app files in SD card, you should use File[] getExternalFilesDirs (String type) method in Context class. Generally, second returned path would be the storage path for microSD card (if any).

On my phone, second path returned was /storage/sdcard1/Android/data/your.application.package.appname/files after passing null as argument to getExternalFilesDirs (String type). But path may vary on different phones, different Android versions.

Both File getExternalStorageDirectory () and File getExternalStoragePublicDirectory (String type) in Environment class may return SD card directory or internal memory directory depending on your phone's model and Android OS version.

Because According to Official Android Guide external storage can be

removable storage media (such as an SD card) or an internal (non-removable) storage.

The Internal and External Storage terminology according to Google/official Android docs is quite different from what we think.

Breathtaking answered 1/8, 2016 at 16:29 Comment(0)
L
0

yes, it may work in KITKAT.

above KITKAT+ it will go to internal storage:paths like(storage/emulated/0).

please think, how "Xender app" give permission to write in to external sd card.

So, Fortunately in Android 5.0 and later there is a new official way for apps to write to the external SD card. Apps must ask the user to grant write access to a folder on the SD card. They open a system folder chooser dialog. The user need to navigate into that specific folder and select it.

for more details, please refer https://metactrl.com/docs/sdcard-on-lollipop/

Leslee answered 7/12, 2018 at 6:41 Comment(0)
G
-2

Try using

new File(Environment.getExternalStorageDirectory(),"somefilename");

And don't forget to add WRITE_EXTERNAL STORAGE and READ_EXTERNAL STORAGE permissions

Grouch answered 6/3, 2014 at 9:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.