共享文件是否需要提供者导出或授予grantUriPermission()权限

7

我一直在尝试解决从外部存储目录共享图像的问题。它在大多数设备上都可以工作,但在其他设备上无法工作。 我已经: 1. 添加了一个扩展FileProvider的类:

public class GenericFileProvider extends FileProvider {}

在标签下的AndroidManifest.xml文件中添加了标签:

 <provider
            android:name=".Utils.GenericFileProvider"
            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>

你需要在res/xml文件夹中创建一个provider_paths.xml文件:

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

我的分享图片的函数如下:

 private fun shareInEmail() {
    val filename = "Avoir+$timeStamp.jpg"
    val filelocation = File(Environment.getExternalStorageDirectory().getAbsolutePath(), filename)
    val path : Uri = FileProvider.getUriForFile(
        requireContext(),
        context!!.applicationContext.packageName.toString() + ".provider",
        filelocation
    )

    context!!.grantUriPermission(
        "com.ideasfactory.mjcprojet",
        path,
        Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
    )

    val emailIntent = Intent(Intent.ACTION_SEND)
    emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
    // set the type to 'email'
    emailIntent.type = "vnd.android.cursor.dir/email"
    emailIntent.setType("text/plain")
    emailIntent.type = "application/jpg"
    val to = arrayOf("contact@mjclambres.fr")
    emailIntent.putExtra(Intent.EXTRA_EMAIL, to)
    emailIntent.putExtra(Intent.EXTRA_BCC, arrayOf<String>())
    // the attachment
    emailIntent.putExtra(Intent.EXTRA_STREAM, path)
    // the mail subject
    emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Transaction")
    emailIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    startActivity(Intent.createChooser(emailIntent, "Transaction"))


}

所有之前的步骤都参考了这个答案:[android.os.FileUriExposedException: file:///storage/emulated/0/test.txt exposed beyond app through Intent.getData()]

我不知道我错过了什么? 我的日志显示:

E/DatabaseUtils: Writing exception to parcel
java.lang.SecurityException: Permission Denial: reading com.ideasfactory.mjcprojet.Utils.GenericFileProvider uri content://com.ideasfactory.mjcprojet.provider/external_files/Avoir%2B2020-06-11%2011%3A08.jpg from pid=12602, uid=1000 requires the provider be exported, or grantUriPermission()
    at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:729)
    at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:602)
    at android.content.ContentProvider$Transport.enforceFilePermission(ContentProvider.java:593)
    at android.content.ContentProvider$Transport.openTypedAssetFile(ContentProvider.java:507)
    at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:307)
    at android.os.Binder.execTransactInternal(Binder.java:1021)
    at android.os.Binder.execTransact(Binder.java:994)

我将感激你们的所有帮助


1
你不需要扩展FileProvider,可以直接使用它。 - blackapps
@blackapps 我直接使用了,但仍然不起作用。 - Lady Geraldine Villamil Guerre
然后在这里也更改代码。这样我们就知道你做了什么。 - blackapps
@blackapps,你是对的,抱歉。在调查后我又提了一个问题。如果你能看到它,我会很感激的,链接 - Lady Geraldine Villamil Guerre
5个回答

8
安卓系统抛出安全异常的原因是因为它试图访问共享资源,以便向用户展示即将共享的内容预览。
最好的方法是使用 Intent.setClipData 来共享资源,除此之外还要使用 Intent.putExtra(Intent.EXTRA_STREAM, fileUri)
您的代码应该如下所示:
...
// the attachment
emailIntent.setClipData(ClipData.newRawUri("", path))
emailIntent.putExtra(Intent.EXTRA_STREAM, path)
...

如果您需要更多参考,请查看FileProvider类。

5
这解决了我的问题。 - Parth Patel
在我的情况下仍然抛出异常。我还尝试了ClipData.newPlainText("label", "text")作为一个测试,以防Android在从复杂对象创建clipdata时遇到困难。 - undefined

3

嘿,我遇到了同样的问题,经过很多研究后,我发现问题出在provider_paths.xml文件中。对于许多不同的情况,我都得到了不同的路径。使用以下代码更改您的provider_paths.xml文件:

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

  <cache-path name="cache" path="." />
  <external-path name="external" path="." />

  <root-path name="root" path="." />
  <files-path name="my_images" path="/" />
  <files-path name="my_images" path="myfile/"/>
  <files-path name="files" path="." />

  <external-path name="external_files" path="." />
  <external-path name="images" path="Pictures" />
  <external-path name="my_images" path="." />
  <external-files-path name="images" path="Pictures"/>
  <external-files-path name="camera_image" path="Pictures/"/>
  <external-files-path name="external_files" path="." />
  <external-files-path name="my_images" path="my_images" />

  <external-cache-path name="external_cache" path="." />
</paths>

2
非常感谢您的帮助。我是Android的新手,我想知道如何访问my_images名称和myfile路径。在我的Kotlin类中,我使用File(Environment.getExternalStorageDirectory().getAbsolutePath(), filename),但我不知道如何在xml文件中实现。再次感谢任何帮助! - Lady Geraldine Villamil Guerre

2
如果您只有一个URI需要分享,请使用此方法。
val shareCompat = ShareCompat
                .IntentBuilder(Your activity)
                .setType("mimeType")
                .addStream(uri)
                .setChooserTitle("Share")
                .startChooser()

如果您有多个文件需要共享,请像下面一样逐个添加所有文件:

uris.forEach { shareCompat.addStream(it) }

在此之后调用shareCompat.startChooser()


2

试试这个

Intent intentShareFile = new Intent(Intent.ACTION_SEND);

    intentShareFile.setType(URLConnection.guessContentTypeFromName(file.getName()));

    Uri uri = FileProvider.getUriForFile(this, "yourpackage", file);

    intentShareFile.putExtra(Intent.EXTRA_STREAM, uri);

    List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(intentShareFile, PackageManager.MATCH_DEFAULT_ONLY);
    for (ResolveInfo resolveInfo : resInfoList) {
        String packageName = resolveInfo.activityInfo.packageName;
        grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
    }

1
查询所有 Android 11 及以上版本的应用程序包需要特殊权限,称为“QUERY_ALL_PACKAGES”。并且需要经过批准。更多详情 - Waqas Younis

1
  Intent intentShareFile = new Intent(Intent.ACTION_SEND);

         intentShareFile.setType("application/pdf");

         Uri uri = FileProvider.getUriForFile(this,  getApplicationContext().getPackageName() + ".provider", pdf);
         intentShareFile.putExtra(Intent.EXTRA_STREAM, uri);

         List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(intentShareFile, PackageManager.MATCH_DEFAULT_ONLY);
         for (ResolveInfo resolveInfo : resInfoList) {
             String packageName = resolveInfo.activityInfo.packageName;
             grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
         }
         startActivity(Intent.createChooser(intentShareFile, "Share PDF using.."));

2
你好,欢迎来到 Stack Overflow。请不要只提供代码作为答案。在这种情况下,您可以解释一下为什么 OP 会收到错误,并说明这样做如何解决问题。 - Jeremy Caney

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