使用FileProvider从相册选择图像文件

19

在为Android N编译时,我遇到了一个关于FileProvider的问题。我需要让用户从相册中选择图片/使用相机拍照并将其裁剪为正方形。

我已经成功地实现了使用相机拍摄图像的FileProvider,但是我在从相册中选择图片时遇到了严重问题。问题在于相册中有很多来自不同位置的文件,例如我得到了以下异常:

  java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/6133-3766/DCIM/100ANDRO/DSC_0035.JPG
所以问题是,我应该在 file_paths.xml 中放置什么内容才能访问 /storage/ 中的任何地方。我不能依赖于精确路径,因为可能会有来自 WhatsApp 和类似应用程序的图片,例如 WhatsApp 图像获取此路径:
/storage/emulated/0/WhatsApp/Media/WhatsApp Images/IMG-20160821-WA0000.jpg

我已经通过空路径成功解决了问题:

<external-path name="external_storage" path=""/>

根据文档,这与Environment.getExternalStorageDirectory()类似。

但是仍然无法处理存储在/storage/SOME_DIR/中的图像。请帮忙。


你的应用程序的minSDK版本是多少? - Chintan Soni
2
如果是19,我认为SAF(存储访问框架)将是您的解决方案。https://developer.android.com/guide/topics/providers/document-provider.html并且在此处展示演示:http://www.techotopia.com/index.php/An_Android_Storage_Access_Framework_Example - Chintan Soni
从图库中选择内容时,请选择MIME类型为“image / *”,而不是选择“文件”..建议检查意图过滤器以获取照片内容类型。通过这种方式,您可以选择一张照片,将响应作为URI类型获取,并在确实需要路径的情况下请求URI对象的绝对路径。示例在此答案中https://dev59.com/xWfWa4cB1Zd3GeqPlefK - Robert Rowntree
允许用户从意图过滤器中选择相册或相机作为选择器,而不需要将FileProvider作为解决方案的核心... - Robert Rowntree
1
优先条件是加载位图。优先条件是从MediaStore上的正确“音量”解析内容。contentResolver的详细信息取决于媒体源(例如:sdCard vs internalStore vs gallery)...请参阅https://developer.android.com/reference/android/provider/MediaStore.Images.Media.html中的“getBitmap”...https://dev59.com/Dl0a5IYBdhLWcg3w6sZk contentResolve-to-bitmap样本,我认为您已经接近了。 - Robert Rowntree
显示剩余5条评论
2个回答

5
我认为这个问题是基于一个误解。 FileProvider 的目的是为了授予(外部应用程序)访问由你的应用程序已经控制的文件。
通过使用自己的文件提供程序,您将永远无法成功地访问外部应用程序拥有的文件。
如果外部应用选择这样做,它需要使用文件提供程序来授予您该访问权限。
似乎这就是问题所在。如果我没有理解您的问题,请告诉我,但是如果我理解了,您想要做的事情就不会起作用。

我正在尝试从图库中选择一张图片,并使用裁剪意图进行裁剪。有些图片可以工作,有些图片则不行。 - Evgeniy Mishustin
我认为,当我触发PICK意图时看到的所有图像都是已经授权给我的应用程序的,但是当我触发裁剪Intent时,我应该授予对这些图像的访问权限。而且有一些路径是我无法授权访问的。 - Evgeniy Mishustin

4

同意@x-code的回答,您描述的问题不是很清楚,但如果您尝试访问另一个应用程序的内部数据,则必须获得相应的权限。

文件属于您的应用程序,并且在用户卸载应用程序时应该被删除。尽管这些文件从技术上来说可以被用户和其他应用程序访问,因为它们存储在外部存储中,但实际上它们是没有提供给用户使用价值的文件,只能在您的应用程序内使用。

实际上,我在文档中发现SDK版本24现在已经更新了许多方案,并且在处理文件时有了重大变化,从文档中可以看到关于file://的问题描述如下:

将file:// URI传递到包域之外可能会导致接收者无法访问路径。因此,尝试传递file:// URI会触发FileUriExposedException。共享私有文件内容的推荐方法是使用FileProvider。

出于安全原因,强烈建议使用Content://而不是使用file://,因此应使用ContentProvider而不是FileProvider

以下是使用它的简单示例:

AndroidMenifest.xml中:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    <application
        ...
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest> 

然后在res文件夹下的xml文件夹中创建一个_paths.xml提供程序文件。如果不存在该文件夹,则需要创建。

文件的内容如下所示。它描述了我们希望使用名称external_files与根文件夹(path=".")共享访问外部存储器。

res/xml/provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

现在要使用它,

Uri photoURI = FileProvider.getUriForFile(MainActivity.this,
        BuildConfig.APPLICATION_ID + ".provider",
        createImageFile());

我从这篇博客中获取了以下内容,请阅读以全面理解。希望能帮到大家。


2
createImageFile()函数内部的代码要小心,因为它应该使用Environment.getExternalStorageDirectory()而不是外部根目录。有关详细信息,请参阅FileProvider - Alejandro Moya

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