透过ClipData.Item.getUri暴露于应用程序之外

143

我正在尝试修复在安卓文件系统添加新功能后出现的问题,但我收到了以下错误信息:

android.os.FileUriExposedException: file:///storage/emulated/0/MyApp/Camera_20180105_172234.jpg exposed beyond app through ClipData.Item.getUri()

所以我希望有人能帮我修复这个问题 :)

谢谢

private Uri getTempUri() {
    // Create an image file name
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
    String dt = sdf.format(new Date());
    imageFile = null;
    imageFile = new File(Environment.getExternalStorageDirectory()
            + "/MyApp/", "Camera_" + dt + ".jpg");
    AppLog.Log(
            TAG,
            "New Camera Image Path:- "
                    + Environment.getExternalStorageDirectory()
                    + "/MyApp/" + "Camera_" + dt + ".jpg");
    File file = new File(Environment.getExternalStorageDirectory() + "/MyApp");
    if (!file.exists()) {
        file.mkdir();
    }
    imagePath = Environment.getExternalStorageDirectory() + "/MyApp/"
            + "Camera_" + dt + ".jpg";
    imageUri = Uri.fromFile(imageFile);
    return imageUri;
}

3
Android OS 文件URI暴露异常(FileUriExposedException):文件“/storage/emulated/0/test.txt”被公开暴露 - CommonsWare
4个回答

262

对于sdk 24及以上版本,如果您需要获取应用存储之外文件的Uri,您将遇到此错误。@eranda.del的解决方案允许您更改策略以允许此操作,并且可以正常工作。

但是,如果您想遵循Google的指导方针而不必更改应用程序的API策略,则必须使用FileProvider。

首先,要获取文件的URI,您需要使用FileProvider.getUriForFile()方法:

Uri imageUri = FileProvider.getUriForFile(
            MainActivity.this,
            "com.example.homefolder.example.provider", //(use your app signature + ".provider" )
            imageFile);

然后您需要在Android清单文件中配置您的提供者:

<application>
  ...
     <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.example.homefolder.example.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <!-- ressource file to create -->
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths">  
        </meta-data>
    </provider>
</application>

在“authorities”中使用与getUriForFile()方法的第二个参数相同的值(应用程序签名+“.provider”)。

最后,您需要创建资源文件:“file_paths”。此文件需要在res/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>

165
“让开发者的生活更轻松” - 这是每个新API的标语,对吧 Google? - Kirill Karmazin
19
Brendon提供的解决方案是正确的,但它不能直接复制粘贴,你需要检查它是否适合你的需求,例如,当你想要使用Environment.getExternalStorageDirectory()时需要使用<external-path ...,而当你需要Context.getFilesDir()时则需要使用<files-path ...。你最好先查看官方文档:https://developer.android.com/reference/android/support/v4/content/FileProvider - Kirill Karmazin
10
如果您正在使用androidx(新的支持命名空间),请注意,您可以在此处找到适当的文档(https://developer.android.com/reference/androidx/core/content/FileProvider)。 - Rodrigo García
37
请注意,在androidx中应为:android:name="androidx.core.content.FileProvider" - smörkex

176
在开始相机或文件浏览之前添加以下代码块。
    StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
    StrictMode.setVmPolicy(builder.build());
请查看推荐链接严格模式及其解释,其中包含所有用法和技术细节。strict mode

35
这不是解决问题的正确方式,这将基本上移除严格模式政策,并忽略安全警告。 - Ahsan Zaheer
这方面有一些利弊。请参考以下链接以获取更多信息: https://dev59.com/5nXYa4cB1Zd3GeqP5mOz - eranda.del
1
如果有任何问题,请让我知道 :)它能够正常工作。 - Hanan
代码已经解决了我的异常问题。然而,这里的顶部评论说这段代码并不是正确解决方法。有人知道正确的方法吗? - Shane Sepac
8
谷歌厌恶程序员。 - Joubert Vasconcelos
3
谢谢!这对我有用。但我仍会调查为什么清单中的提供程序设置对我无效。 - Tony_Bielo

4
AndroidManifest中不需要配置提供程序(provider),只需允许相机和存储权限。使用以下代码启动相机:
final int REQUEST_ACTION_CAMERA = 9;
void openCamra() {
Intent cameraImgIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

cameraImgIntent.putExtra(MediaStore.EXTRA_OUTPUT,
                FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID +".provider",
                new File("your_file_name_with_dir")));
startActivityForResult(cameraImgIntent, REQUEST_ACTION_CAMERA);
}

在拍摄图像后,您可以在以下位置找到捕获的图像:

"your_file_name_with_dir"

最好在AndroidManifest中解释提供程序的配置,而不是提供其他代码。 - fury.slay
这对我在AndroidManifest中的<provider>标签起作用。 - NehaK

0

添加代码

onCreate(Budle savedInstancesState){
    if (Build.VERSION.SDK_INT >= 24) {
        try {
            Method m = StrictMode.class.getMethod("disableDeathOnFileUriExposure");
            m.invoke(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

获取图像.jpg时出现文件格式不支持的错误。 - Danny RDX
这个技巧在Android 11上已经不再适用了 :) - Nicholas TJ

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