Android中openInputStream触发SecurityException权限拒绝问题求助
问题解决:SAF Uri读取时的SecurityException错误
问题根源
你遇到的SecurityException是因为操作的是Storage Access Framework(SAF) 生成的Uri,这类Uri不能通过常规的READ_EXTERNAL_STORAGE权限访问,必须依赖SAF的权限授予机制:
- 直接申请
MANAGE_DOCUMENTS权限不可行,这是系统级权限,第三方App无法正常获取 - 你的代码中直接在读取阶段调用
takePersistableUriPermission,但该方法仅在Uri通过SAF选择器(如ACTION_OPEN_DOCUMENT)获取且携带了持久权限标记时才能生效
分场景解决方案
场景1:Uri是自己App通过SAF选择器获取的
先确保你用正确的方式发起文件选择,并且获取持久权限:
1. 发起SAF文件选择Intent
private static final int REQUEST_CODE_OPEN_DOC = 1001; // 在Activity/Fragment中调用 public void openSAFFilePicker() { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("*/*"); // 根据需求设置具体MIME类型,比如application/octet-stream // 必须添加这两个标记,才能后续申请持久权限 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); startActivityForResult(intent, REQUEST_CODE_OPEN_DOC); }
2. 在回调中获取Uri并申请持久权限
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_OPEN_DOC && resultCode == RESULT_OK) { Uri uri = data.getData(); if (uri != null) { // 从Intent中提取权限标记,避免硬编码 int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); // 申请持久权限,后续重启App也能访问该Uri getContentResolver().takePersistableUriPermission(uri, takeFlags); // 此时再调用你的readUriRaw方法即可正常读取 byte[] fileData = readUriRaw(uri, this); } } }
场景2:Uri是从其他App传递过来的
这种情况下,必须由传递Uri的App在Intent中添加FLAG_GRANT_READ_URI_PERMISSION标记,你的App才能临时访问该Uri。如果对方App没有添加这个标记,你无法直接访问,只能提示用户通过你的App的SAF选择器重新选择文件。
修正后的readUriRaw方法
移除读取阶段的takePersistableUriPermission调用(该操作应该在获取Uri时完成),并完善流的关闭逻辑:
private static final String LOG_TAG = "FileReader"; public static byte[] readUriRaw(Uri uri, Context context) { if (uri == null || context == null) { return null; } InputStream contentStream = null; ByteArrayOutputStream buffer = new ByteArrayOutputStream(); try { // 直接打开输入流,前提是Uri已被授予访问权限 contentStream = context.getContentResolver().openInputStream(uri); int len; byte[] data = new byte[16384]; while ((len = contentStream.read(data, 0, data.length)) != -1) { buffer.write(data, 0, len); } return buffer.toByteArray(); } catch (SecurityException ex) { Log.e(LOG_TAG, "权限不足,无法访问Uri: " + uri.toString(), ex); return null; } catch (IOException ex) { Log.e(LOG_TAG, "读取Uri内容失败", ex); return null; } finally { // 确保流资源被释放 if (contentStream != null) { try { contentStream.close(); } catch (IOException e) { Log.w(LOG_TAG, "关闭输入流失败", e); } } try { buffer.close(); } catch (IOException e) { Log.w(LOG_TAG, "关闭输出流失败", e); } } }
关键注意事项
- Android 10及以上版本,常规外部存储权限对SAF Uri完全无效,必须依赖SAF的权限机制
- 持久化权限会保留到用户在系统设置中取消你的App文件访问权限,或删除对应文件为止
内容的提问来源于stack exchange,提问作者Meriem Arrouj
相关产品推荐
相关产品推荐

