首页 文章

用于存储文件的Android Open External Storage目录(sdcard)

提问于
浏览
32

我想打开外部存储目录路径以编程方式保存文件 . 我试过但没有获得SD卡路径 . 我怎么能这样做?有什么解决方案吗?

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

要么

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

我尝试从两个方法上面获取路径,但两者都指向内部存储器 .

当我们打开存储内存时,如果sdcard显示,它将显示如下 -
enter image description here

设备存储和SD存储卡 .

我想通过编码获得sd内存路径 . 我已经在清单中给予了许可 -

<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" />

8 回答

  • 18

    我一直有同样的问题!

    要获得内部SD卡,您可以使用

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

    要获得外部SD卡,您可以使用

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

    在运行代码时

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

    完美运作!

  • -1

    内部存储在API中称为“外部存储” .

    Environment文档中所述

    注意:不要在这里混淆“外部”这个词 . 此目录最好被视为媒体/共享存储 . 它是一个文件系统,可以容纳相对大量的数据,并在所有应用程序之间共享(不强制执行权限) . 传统上,这是一张SD卡,但它也可以作为与受保护的内部存储不同的设备中的内置存储实现,并且可以作为文件系统安装在计算机上 .

    要区分“Environment.getExternalStorageDirectory()”是否实际返回物理内部或外部存储,请调用Environment.isExternalStorageEmulated() . 如果它是模仿的,那么它就是内部的 . 在具有内部存储和SD卡插槽的较新设备上,Environment.getExternalStorageDirectory()将始终返回内部存储 . 在仅使用SD卡作为媒体存储选项的旧设备上,它将始终返回SD卡 .

    无法使用当前的Android API检索所有存储 .

    我在下面的答案中创建了一个基于Vitaliy Polchuk方法的助手

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

    NOTE: starting KitKat secondary storage is accessible only as READ-ONLY ,您可能希望使用以下方法检查可写性

    /**
     * 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类

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

    希望它对你有用:

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

    这将给你sdcard路径:

    File path = Environment.getExternalStorageDirectory();
    

    试试这个:

    String pathName = "/mnt/";
    

    或试试这个:

    String pathName = "/storage/";
    
  • 58

    补充 rijul gupta 答案:

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

    将@ rijul的答案向前推进,它在棉花糖及以上版本中无效:

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

    我想打开外部存储目录路径以编程方式保存文件 . 我试过但没有获得SD卡路径 . 我怎么能这样做?有什么解决方案吗?

    要将应用程序文件存储在SD卡中, shouldContext 类中使用 File[] getExternalFilesDirs (String type) 方法 . 通常,第二个返回路径将是microSD卡的存储路径(如果有的话) .

    在我的手机上,在将 null 作为参数传递给 getExternalFilesDirs (String type) 之后,返回的第二条路径为 /storage/sdcard1/Android/data/your.application.package.appname/files . 但路径可能因手机不同而不同,不同的Android版本 .

    Environment 类中的 File getExternalStorageDirectory ()File getExternalStoragePublicDirectory (String type) 都可能返回SD卡目录或内部存储器目录,具体取决于您的手机型号和Android操作系统版本 .

    因为Official Android Guide外部存储可以

    可移动存储介质(如SD卡)或内部(不可移动)存储 .

    根据谷歌/官方Android文档的内部和外部存储术语是我们认为的 quite different .

  • 2

    是的,它可能在KITKAT中有效 .

    在KITKAT之上它将进入内部存储:路径如(storage / emulated / 0) .

    请想一想,“Xender app”如何允许写入外部SD卡 .

    因此,幸运的是,在Android 5.0及更高版本中,有一种新的官方方式可以让应用程序写入外部SD卡 . 应用必须要求用户授予对SD卡上文件夹的写入权限 . 他们打开一个系统文件夹选择器对话框 . 用户需要导航到该特定文件夹并选择它 .

    有关详细信息,请参阅https://metactrl.com/docs/sdcard-on-lollipop/

  • 0

    尝试使用

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

    并且不要忘记添加WRITE_EXTERNAL STORAGE和READ_EXTERNAL STORAGE权限

相关问题