【问题标题】:Android: How to get a content:// URI for a file in the external storage PUBLIC directoryAndroid:如何获取外部存储 PUBLIC 目录中文件的 content:// URI
【发布时间】:2016-10-23 12:52:15
【问题描述】:

我已关注this Google tutorial,开始使用new Intent(MediaStore.ACTION_IMAGE_CAPTURE) 捕获图像。本教程建议使用带有getExternalStoragePublicDirectory 的公共目录,这对我的应用程序非常有用。但随后他们的示例改为使用getExternalFilesDir。然后用MediaStore.EXTRA_OUTPUT 将文件的URI 传递给intent,我have to get a content URI 因为我想以Android N 为目标。在以N 为目标之前,我只需传递一个file:// URI,除了Google 之外的所有人都很高兴。现在在 N 上,我开始收到 FileUriExposedException,它似乎是 not very favorable

所以鉴于我有这样的文件...

private File createImageFile() throws IOException {
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    File storageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyAppFolder");
    if (!storageDir.exists() && !storageDir.mkdir())
        Log.w(TAG, "Couldn't create photo folder: " + storageDir.getAbsolutePath());
    File image = new File(storageDir, timeStamp + ".jpg");
    mCurrentPhotoPath = image.getAbsolutePath();
    return image;
}

...我可以使用公共图片目录的内置提供程序来获取内容 URI 吗?如果有怎么办?

我尝试过类似的东西

takePictureIntent.putExtra(
    MediaStore.EXTRA_OUTPUT, 
    FileProvider.getUriForFile(this, MediaStore.AUTHORITY, createImageFile()));

但它只是抛出

IllegalArgumentException:缺少 android.support.FILE_PROVIDER_PATHS 元数据

那个权威是正确的吗?如果我必须使用自己的提供程序来共享公共文件,那么我可以在 FILE_PROVIDER_PATHS 元数据中指定什么路径?所有的options I can find 都是私有目录。

【问题讨论】:

  • “这个权限正确吗?” ——不,因为你不是MediaStore。 “如果我必须使用自己的提供程序来共享公共文件,那么我可以在我的 FILE_PROVIDER_PATHS 元数据中指定什么路径?” -- <external-path> 为您提供外部存储的根。 FileProvider 无法指定您要专门使用的目录。
  • @CommonsWare 如果 FileProvider 无法使用该目录,那么我唯一的选择是使用私有目录还是停止以 N 为目标?这似乎很愚蠢。感谢您的建议。
  • “如果 FileProvider 无法使用该目录”——这不是我写的。话虽如此,由于您所需子目录的详细信息因设备而异,因此尝试使用<external-path> 会有风险。 “那么我唯一的选择是使用私人目录还是停止定位 N?” - 不。你可以写你自己的ContentProvider。或者,您可以为我的StreamProvider 编写一个插件。或者您可以在外部存储上使用自定义目录,您可以从外部存储根目录控制其路径(尽管这可能是您所说的“私有目录”)。
  • 感谢 CommonsWare 并对我的误解深表歉意。在我找到更好的方法之前,我将在可以使用FileProvider 托管的目录中创建文件。然后在onActivityResult 中,我可以将文件复制到外部存储公共目录中的最终存放位置。自定义和第三方 ContentProvider 似乎对这项工作有点繁重。

标签: android android-intent android-contentprovider android-7.0-nougat


【解决方案1】:

TL:DR <external-path name="MyAppFolder" path="." /> 有效,但安全吗?

您需要创建自己的FileProvider。值得一读它的documentation,但这里有一个简短的概述。

正如@CommonsWare 提到的,您需要将示例中的MediaStore.AUTHORITY 替换为"com.example.fileprovider",这是您将在清单中定义的权限,如下所示...

在 AndroidManifest 的 <application> 标记中,键入以下内容:

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.example.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

您得到的例外是由于没有&lt;meta-data&gt; 标记链接到一个xml 文件,该文件包含您的FileProvider 允许触摸的所有路径。但这正是我一直在为自己苦苦挣扎的文件中的内容。

在您链接的教程即将结束时,在 Save the Full-size Photo 部分的末尾,Google 为这个 file_paths.xml 提供的示例是:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="my_images" path="Android/data/com.example.package.name/files/Pictures" />
</paths>

"my_images" 在您的示例中将替换为 "MyAppFolder"。在那个教程中谷歌声称

路径组件对应于使用 Environment.DIRECTORY_PICTURES 调用时 getExternalFilesDir() 返回的路径。确保将 com.example.package.name 替换为应用的实际包名称。

...输入它让我意识到这不是指您和我正在寻找的公共图片目录。这就解释了为什么使用该路径会产生此异常:

java.lang.IllegalArgumentException:未能找到包含 /storage/emulated/0/Pictures/MyAppFolder/{filename}.jpg 的配置根目录

我将把它留在这里以供将来参考,然后继续回答您的大问题 - 您可以指定什么路径以允许其他应用与您的应用针对 API 级别创建的公共图片目录中的文件进行交互24+?

正如@CommonsWare 所说,“所需子目录的详细信息因设备而异”,因此您无法声明公共图片目录的路径,但我确实找到了一种可行的方法。

&lt;external-path name="MyAppFolder" path="." /&gt; 将允许您的FileProvider 向其他应用程序(如相机)提供内容 URI,然后这些应用程序可以读取和写入它解析的文件。

如果这有任何危险或不利之处,我很想听听。

【讨论】:

  • 我投票赞成“这是安全的”。根据清单 XML,提供者似乎被很好地锁定了;它不会被导出,因此其他应用只能根据意图中的明确授权 (Intent.FLAG_GRANT_READ_URI_PERMISSION) 获得对您文件的权限。
【解决方案2】:

这是我在我的应用程序中使用的图片公共目录。

  1. 像这样创建文件:

    File theFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "YOUR_APP_NAME" + File.separator + "YOUR_FILE_NAME");
    
  2. 在文件提供者的元数据中指定以下路径:

    <paths>
        <external-path name="secure_name" path="Pictures/YOUR_APP_NAME" />
    </paths>
    

"secure_name" 用于隐藏您出于安全原因共享的子目录的名称(在本例中为“YOUR_APP_NAME”)。

“图片”对应Environment.DIRECTORY_PICTURES

  1. 获取UriIntent一起发送:

    final Uri theUri = FileProvider.getUriForFile(activity,
                        getString(R.string.fileprovider_authorities),
                        theFile);
    theIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    theIntent.putExtra(MediaStore.EXTRA_OUTPUT, theUri);
    

【讨论】:

    猜你喜欢
    • 2016-07-31
    • 1970-01-01
    • 1970-01-01
    • 2021-12-09
    • 2017-07-04
    • 2016-08-21
    • 1970-01-01
    • 2014-04-08
    • 2014-09-07
    相关资源
    最近更新 更多