升级至Android API 33后存储权限问题求助
问题背景
基于Ionic、Angular和Capacitor开发的应用,使用cordova-diagnostic-plugin实现图片下载功能,将目标Android SDK版本升级至33后,存储功能出现异常。
当前表现
- 点击下载图片时,弹出存储权限请求对话框,提示允许应用访问设备文件
- 按文档重新请求权限时,权限对话框不再弹出,日志输出如下:
Requesting permission for android.permission.READ_EXTERNAL_STORAGE
Requesting permissions
Notifying listeners for event pause
No listeners found for event pause
App paused
Unable to find a Capacitor plugin to handle permission requestCode, trying Cordova plugins 0
预期表现
点击下载按钮后,图片能正常下载并存储到设备中。
环境信息
- Ionic CLI: 7.1.1
- Ionic Framework: @ionic/angular 7.2.0
- @angular/cli: 16.1.0
- @ionic/angular-toolkit: 9.0.0
- Capacitor相关:
- Capacitor CLI: 5.2.2
- @capacitor/android: 5.2.2
- @capacitor/core: 5.2.2
- @capacitor/ios: 5.2.2
- 系统环境:
- NodeJS: v18.10.0
- npm: 8.19.2
- 目标Android SDK版本:33
- 插件版本:
cordova.plugins.diagnostic: ^7.1.2
相关代码片段
const destinationUri = this.platform.is("android") ? "file:///storage/emulated/0/" : this.ionicFile.dataDirectory; const blob: Blob = res.data; if (this.platform.is("android")) { try { await this.ionicFile.checkDir(destinationUri, "dcim/WXS ONE"); } catch { await this.ionicFile.createDir(destinationUri, "dcim/WXS ONE", false); } }
解决方案
1. 适配Android 13的存储权限逻辑
Android 13(SDK33)废弃了READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限,针对图片文件需使用READ_MEDIA_IMAGES权限(写入无需单独申请,只要是文件创建者或拥有读取权限即可):
- 首先升级
cordova-diagnostic-plugin到支持Android 13的版本(至少v7.1.3,建议最新版):
npm install cordova.plugins.diagnostic@latest
- 修改权限请求代码,区分Android版本:
import { Diagnostic } from '@ionic-native/diagnostic/ngx'; async requestStoragePermissions() { if (this.platform.is('android')) { const androidVersion = parseInt(this.platform.version().split('.')[0], 10); const permissions = androidVersion >= 13 ? [Diagnostic.permission.READ_MEDIA_IMAGES] : [Diagnostic.permission.READ_EXTERNAL_STORAGE, Diagnostic.permission.WRITE_EXTERNAL_STORAGE]; const status = await this.diagnostic.requestPermissions(permissions); const hasPermission = androidVersion >=13 ? status[Diagnostic.permission.READ_MEDIA_IMAGES] === Diagnostic.permissionStatus.GRANTED : status[Diagnostic.permission.READ_EXTERNAL_STORAGE] === Diagnostic.permissionStatus.GRANTED; if (hasPermission) { this.downloadImage(); } else { console.log('存储权限被拒绝,无法下载图片'); } } }
2. 替换文件存储方式(推荐)
使用Capacitor官方的@capacitor/filesystem插件替代Cordova文件插件,该插件原生适配Android 13存储机制:
- 安装插件并同步项目:
npm install @capacitor/filesystem npx cap sync
- 修改存储逻辑,将图片保存到系统公共图片目录:
import { Filesystem, Directory } from '@capacitor/filesystem'; async saveImageToDevice(blob: Blob) { if (this.platform.is('android')) { // 将Blob转换为base64格式 const base64 = await new Promise<string>((resolve) => { const reader = new FileReader(); reader.onloadend = () => resolve(reader.result as string); reader.readAsDataURL(blob); }); // 保存到公共图片目录的自定义子文件夹 await Filesystem.writeFile({ path: 'WXS ONE/downloaded-image.jpg', data: base64.split(',')[1], // 移除base64前缀 directory: Directory.Pictures, recursive: true }); } else { // iOS端存储逻辑 await Filesystem.writeFile({ path: 'WXS ONE/downloaded-image.jpg', data: (await this.blobToBase64(blob)).split(',')[1], directory: Directory.Data, recursive: true }); } }
3. 配置AndroidManifest.xml权限
在android/app/src/main/AndroidManifest.xml中添加版本区分的权限声明:
<!-- Android 13及以上权限 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- 低版本兼容权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
4. 修复Capacitor与Cordova插件兼容问题
日志中Unable to find a Capacitor plugin to handle permission requestCode是因为Capacitor 5对Cordova插件权限处理有变更,升级插件后重新同步Android项目:
npx cap sync android
内容的提问来源于stack exchange,提问作者Jitendera Kumar

