FileProvider 和辅助外部存储

Posted

技术标签:

【中文标题】FileProvider 和辅助外部存储【英文标题】:FileProvider and secondary external storage 【发布时间】:2017-03-12 02:46:39 【问题描述】:

如何使用 FileProvider 从 SECONDARY 外部存储提供文件?

FileProvider 的当前实现只处理ContextCompat.getExternalFilesDirs 返回的第一个目录

...    
 else if (TAG_EXTERNAL_FILES.equals(tag)) 
   File[] externalFilesDirs = ContextCompat.getExternalFilesDirs(context, null);
   if (externalFilesDirs.length > 0) 
       target = externalFilesDirs[0];
   

...

似乎没有办法为FileProvider 定义一个与辅助外部存储路径匹配的<path> 条目...

【问题讨论】:

二级外部存储是什么意思? ContextCompat.getExternalFilesDirs 返回的目录,数组索引 > 0。在大多数设备上,它可能是可移动 sd 卡。 AFAIK, FileProvider 不支持这个。您可以使用my StreamProvider 来安装一些东西,尽管不支持这种“开箱即用”。我已将其添加到StreamProvider 的待办事项列表中,因为您的观点很好。特别是由于这些位置不涉及应用程序权限,因此它们应该是可服务的,至少当它们存在时。正确处理可移动存储不可用但您要求提供它的情况可能会变得棘手。 【参考方案1】:

FileProvider 不支持二级存储,因为下面的代码:

来自 support:support-core-utils:26.1.0 FileProvider 的代码

             else if (TAG_EXTERNAL_FILES.equals(tag)) 
                File[] externalFilesDirs = ContextCompat.getExternalFilesDirs(context, null);
                if (externalFilesDirs.length > 0) 
                    target = externalFilesDirs[0];// Code here, That's why!!!
                
             else if (TAG_EXTERNAL_CACHE.equals(tag)) 

但是,FileProvider 中有一个特殊的 TAG:root-path,官方参考中没有提到。

            if (TAG_ROOT_PATH.equals(tag)) 
                target = DEVICE_ROOT;// DEVICE_ROOT = new File("/");
             else if (TAG_FILES_PATH.equals(tag)) 

所以,root-path 匹配所有路径。

只需在您的 FileProvider xml 中键入此代码,然后 FileProvider 即可处理二级存储中的文件。

<root-path name="root" path="." />

请注意,它可能会泄露您的目录结构。

【讨论】:

【参考方案2】:

为了处理位于外部 SD 卡上的文件,我将 provider_paths.xml 更改为

<paths>
    <external-path path="." name="external_files" />
    <root-path path="." name="sdcard1" />

</paths>

【讨论】:

【参考方案3】:

所以我最终做了以下事情:

尝试通过FileProvider创建Uri,如果失败是因为:

java.lang.IllegalArgumentException: Failed to find configured root that contains

我只是在创建一个常规的 Uri。

这是我的代码:

try 
        uri = FileProvider.getUriForFile(context,
                MY_AUTHORITY_STRING,
                imageFile);
     catch (Exception e) 
        CLog.d(TAG, e);
        uri = Uri.fromFile(imageFile);
    

我不知道为什么,但它正在工作,FileProvider 无法访问文件(因为它在辅助外部存储中),然后在 catch 子句中成功创建了 uri。

奇怪的谷歌...非常奇怪。

【讨论】:

在 7.0+ 中崩溃【参考方案4】:

作为解决方法,您可以使用绝对路径:

<!-- secondary external storage with path /storage/extSdCard -->
<root-path path="/storage/extSdCard/android/data/YOUR_PACKAGE/files/" name="extSdCard" />

<!-- secondary external storage with path /storage/sdcard1  -->
<root-path path="/storage/sdcard1/Android/data/YOUR_PACKAGE/files/" name="sdcard1" />

【讨论】:

对于较新的设备,外部路径取决于插入的 SD 卡,因为它使用参考 ID,因此无法使用绝对路径。【参考方案5】:

答案是……FileProvider 不支持。在 Android 7 中,由于 file:// Uri 方案已弃用,这更是一个问题。

我发出了bug report。

【讨论】:

感谢错误报告和链接,另一个开发人员在那里提供了 FileProvider 的工作子类。

以上是关于FileProvider 和辅助外部存储的主要内容,如果未能解决你的问题,请参考以下文章

注意力机制与外部记忆

使用 FileProvider uri 打开 PDF 文件会打开一个空白屏幕

(计算机组成原理)第七章输入和输出系统-第二节2:外部设备之外存储器(磁盘的基本结构,磁盘阵列)

C:指针基础

安卓内部外部文件存储

Android 中内部存储和外部存储的理解与应用