Android文件提供程序用于外部SD卡

8
我正在使用FileProvider将我的内部文件暴露给Gallery,以使其更加统一,我还通过external-path将我的外部文件放入提供程序中,但对于可移动SD卡中的文件,它不起作用。显示类似“该文件夹未经授权”的消息。
非常感谢您的任何帮助。
谢谢。
5个回答

27

我按照@Gubatron的建议在我的XML中添加了这个根路径,并且它确实起作用了。

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="external_files" path="."/>
    <root-path name="external_files" path="/storage/" />
</paths>

1
惊人的解决方案,拯救了我的Crashlytics! - Hamzeh Soboh
路径应该有不同的名称,否则FileProvider将只会初始化其中一个。 - John Doe

7

让我们来看一下 FileProvider 代码:

    private static PathStrategy parsePathStrategy(Context context, String authority)
        ...
        int type;
        while ((type = in.next()) != END_DOCUMENT) {
            if (type == START_TAG) {
                final String tag = in.getName();
                final String name = in.getAttributeValue(null, ATTR_NAME);
                String path = in.getAttributeValue(null, ATTR_PATH);
                File target = null;
                if (TAG_ROOT_PATH.equals(tag)) {
                    target = buildPath(DEVICE_ROOT, path);
                } else if (TAG_FILES_PATH.equals(tag)) {
                    target = buildPath(context.getFilesDir(), path);
                } else if (TAG_CACHE_PATH.equals(tag)) {
                    target = buildPath(context.getCacheDir(), path);
                } else if (TAG_EXTERNAL.equals(tag)) {
                    target = buildPath(Environment.getExternalStorageDirectory(), path);
                }
                if (target != null) {
                    strat.addRoot(name, target);
                }
            }
        }
        return strat;
    }

FileProvider接受root-path(常量DEVICE_ROOT)标签的绝对路径目录。因此,只需向次外部磁盘中的文件夹添加绝对路径,如下所示:

<root-path path="/storage/extSdCard/Android/data/com.edufii/files/image/" name="image-ext2" />
<root-path path="/storage/extSdCard/Android/data/com.edufii/files/video/" name="video-ext2" />
<root-path path="/storage/extSdCard/Android/data/com.edufii/files/datafile/" name="datafile-ext2" />
<root-path path="/storage/extSdCard/Android/data/com.edufii/files/audio/" name="audio-ext2" />

请注意,官方文档中并未提及<root-path>,因此在未来可能会更改。


2

FileProvider不支持第二个外部存储(例如可移动的SD卡)。这在Android 7及以上版本中更成为问题,因为您不能再使用file:// URI。

我已经在这里提交了一个错误报告。


3
我在AVD模拟器的SD卡存储XML路径文件上尝试了这个方法,并且它起作用了。 </paths> - Gubatron

1

从Android 4.4开始,普通应用程序不允许访问辅助外部存储设备,即SD卡,除非在其特定于包的目录中,即使您已请求WRITE_EXTERNAL_STORAGE权限。

WRITE_EXTERNAL_STORAGE权限只能授予对设备上的主要外部存储的写访问权限。应用程序不得被允许写入辅助外部存储设备,除非使用合成权限允许在其特定于包的目录中进行写入。以这种方式限制写入可以确保系统在卸载应用程序时清理文件。

https://source.android.com/devices/storage/


谢谢!但是我觉得这有点模糊。文档中说,如果外部存储是半永久性的,例如电池仓中的SD卡插槽,则应该将其显示出来。但是你说SD卡是次要的外部存储,而且从4.4版本开始,它不会通过存储API显示。 - stdout
实际上,在 Android 的术语中,“内部存储”和“SD 卡”都可以被称为“外部存储”。以摩托罗拉 Defy 旧手机为例,它只有一个“外部存储”,那就是 SD 卡,所以这个 SD 卡就是“主要外部存储”。Moto G 或某些 Nexus 手机只有“内部存储”而没有 SD 卡,因此“内部存储”在这里是“主要外部存储”。对于三星手机来说,它们既有内部存储也有 SD 卡,这时候内部存储就是“主要外部存储”,而 SD 卡则是“辅助性外部存储”。 - Derek Fung
1
更令事情变得混乱的是,它还涉及到“内部存储(internal storage)”的问题。事实上,所有手机都必须有“内部存储器(internal MEMORY)”,只是大小不同而已。以同样的例子来说,Defy手机的内部存储器很小,只用作“内部存储器(internal storage)”,而新型号通常具有更大的内部存储器,因此只有一部分被分配为“内部存储器(internal storage)”。 - Derek Fung
好的,明白了。还有一个问题……有时我会遇到一个术语,“内置外部存储”。我相信这是指内部存储器中的外部存储器,对吗? - stdout
是的,你说得对。按照我上面的术语,我会说它是在“内部存储器”上的外部存储。 - Derek Fung

0
访问外部SD卡。首先调用 uri.getEncodedPath() 获取编码路径。

" /external_files/3161-3330/WhatsApp/Media/WhatsApp%20Documents/All%20Currency.pdf "

然后使用以下逻辑来获取外部存储的文件路径

public String getFilePath(){
    if (isKitKat && DocumentsContract.isDocumentUri(mContext, uri)) {
                // ExternalStorageProvider
                if (com.android.externalstorage.documents.equals(uri.getAuthority())) {
                    final String docId = DocumentsContract.getDocumentId(uri);
                    final String[] split = docId.split(":");
                    final String type = split[0];
    
                    if ("primary".equalsIgnoreCase(type)) {
                        return Environment.getExternalStorageDirectory() + "/" + split[1];
                    } else {
                        return "/storage" + "/" + split[0] + "/" + split[1];
                    }
               }
    }
}

getFilePath() 将会返回:

/storage/emulated/0/3161-3330/WhatsApp/Media/WhatsApp Documents/All Currency.pdf

其中 uri 路径以

/external_files/

开头,而文件路径则以

/storage/

开头。因此,我们需要添加以下行:

 <root-path name="external_files" path="/storage/" />

@xml/provider_paths.xml


你有没有仔细阅读问题? - Osagui Aghedo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接