Android实现文件分享功能时遇文件访问权限被拒报错
问题核心原因
- Android 7.0(API24)及以上版本系统强制禁止跨应用传递
file://格式的文件Uri,这种直接拼接文件路径的写法不会给接收方应用授予文件访问权限,对方尝试读取文件时会直接被系统拦截,和你是否添加ScopedStorage忽略配置、是否申请读写外部存储权限无关:读写存储权限仅授权你自己的应用访问存储,不会给第三方应用开放对应路径的访问资格。 - 你看到的
Permission to access file is denied报错,本质是接收方应用拿到你传的file://路径后,没有权限直接访问公共存储目录下的文件,被MediaProvider拦截导致的。
修复方案
1. 清单文件注册FileProvider
FileProvider是安卓官方提供的跨应用文件分享组件,专门用来生成带临时访问授权的content://格式Uri,替换不安全的file://传递方式。
在AndroidManifest.xml的<application>标签内部添加以下配置:
<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
2. 配置可分享的文件路径
在项目res目录下新建xml子目录,在其中创建file_paths.xml文件,配置你需要分享的文件所在目录的映射。你当前要分享的文件在Download公共目录,配置如下:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 映射外部存储Download目录 --> <external-path name="download_files" path="Download/"/> <!-- 如果后续需要分享其他目录的文件,对应添加映射即可: 应用内部files目录用<files-path> 应用外部私有存储目录用<external-files-path> 外部存储根目录用<external-path> --> </paths>
3. 修改分享逻辑代码
把原来直接拼接file://的逻辑替换为FileProvider生成授权Uri,同时主动给接收方添加临时读权限:
public void buttonShareFile(View view) { File file = new File(stringFile); if(!file.exists()) { Toast.makeText(this, "No file!", Toast.LENGTH_LONG).show(); return; } Intent intentShare = new Intent(Intent.ACTION_SEND); intentShare.setType("text/csv"); // 生成带授权的content格式Uri Uri shareUri = FileProvider.getUriForFile( this, BuildConfig.APPLICATION_ID + ".fileprovider", file ); intentShare.putExtra(Intent.EXTRA_STREAM, shareUri); // 给目标应用授予该Uri的临时读取权限 intentShare.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity(Intent.createChooser(intentShare, "Share the file....")); }
额外适配注意事项
- Android 13(API33)及以上版本上,旧版
READ_EXTERNAL_STORAGE/WRITE_EXTERNAL_STORAGE权限已经失效,如果分享的是应用自身生成的文件,不需要申请这两个权限,也不需要添加ScopedStorage忽略标记,直接通过FileProvider分享即可正常工作。 - 如果文件由你的应用生成,优先存到应用自身的外部私有目录(
context.getExternalFilesDir()对应路径),再通过FileProvider分享,这种方式完全不需要申请任何存储权限,适配所有安卓版本。
内容的提问来源于stack exchange,提问作者Manuel Torres
相关产品推荐
相关产品推荐

