Android:如何获取内容://外部存储PUBLIC目录中文件的URI

时间:2016-06-21 19:56:15

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

我已跟随this Google tutorial开始使用new Intent(MediaStore.ACTION_IMAGE_CAPTURE)捕获图片的意图。本教程建议将公共目录与getExternalStoragePublicDirectory一起使用,这对我的应用程序非常有用。但他们的例子却使用了getExternalFilesDir。然后使用MediaStore.EXTRA_OUTPUThave to get a content URI将文件的URI传递给意图,因为我想定位Android N.在定位NI之前,只传递一个文件:// URI和所有人,但是谷歌很高兴。现在,我开始得到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都用于私人目录。

2 个答案:

答案 0 :(得分:10)

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>

您获得的例外情况是由于没有<meta-data>标记链接到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所说,“所需子目录的详细信息因设备而异”,因此您无法声明公共图片目录的路径,但我找到了一种有效的方法。

<external-path name="MyAppFolder" path="." />将允许您的FileProvider向其他应用(例如相机)提供内容URI,然后可以读取和写入其解析的文件。

如果这有任何危险或缺点,我很乐意听到。

答案 1 :(得分:4)

以下是我在图片公共目录的应用中使用的内容。

  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>
    
  3. “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);