Ionic7+Capacitor5在Android11+中WRITE_EXTERNAL_STORAGE权限失效求助
解决 Android 11/12 下 Ionic 7 + Capacitor 5 存储权限失效问题
核心原因:Android 11+ 的 Scoped Storage 机制
Android 11(API 30)开始强制启用分区存储,彻底改变了外部存储的访问规则:
WRITE_EXTERNAL_STORAGE权限在 API 30+ 不再弹出授权弹窗,且android:requestLegacyExternalStorage="true"仅当targetSdkVersion ≤30时有效(Capacitor 5 默认 targetSdk 是33,所以这个配置等于无效)- 媒体文件访问用
READ_MEDIA_IMAGES替代旧的READ_EXTERNAL_STORAGE,写入媒体文件无需额外权限(通过系统提供的MediaStoreAPI 操作) - 非媒体文件的公共目录访问,要么通过**存储访问框架(SAF)**让用户手动选择路径,要么直接存在 app 私有目录(私有目录不需要任何权限)
一步步修复方案
1. 修正权限配置
修改 AndroidManifest.xml,针对不同 API 级别做权限适配:
<!-- 媒体权限:Android 13+ 用 READ_MEDIA_IMAGES,旧版本降级用 READ_EXTERNAL_STORAGE --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" android:maxSdkVersion="33" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="29" /> <!-- WRITE_EXTERNAL_STORAGE 仅在 Android 10 及以下需要 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" /> <!-- 只有当 targetSdkVersion ≤30 时,保留这个配置;否则直接删除 --> <application ... android:requestLegacyExternalStorage="true" ...>
2. 调整权限请求逻辑
不同 Android 版本要请求对应的权限,避免无效的权限请求:
import { AndroidPermissions } from '@awesome-cordova-plugins/android-permissions/ngx'; import { Platform } from '@ionic/angular'; async requestStoragePermissions() { if (!this.platform.is('android')) return; const androidVersion = parseInt(this.platform.version().toString()); let permissions = []; if (androidVersion >= 33) { // Android 13+ 仅请求媒体读取权限 permissions.push(AndroidPermissions.PERMISSION.READ_MEDIA_IMAGES); } else if (androidVersion >= 30) { // Android 11-12,分区存储下无需请求 WRITE_EXTERNAL_STORAGE permissions.push(AndroidPermissions.PERMISSION.READ_EXTERNAL_STORAGE); } else { // Android 10 及以下,请求读写权限 permissions.push(AndroidPermissions.PERMISSION.WRITE_EXTERNAL_STORAGE, AndroidPermissions.PERMISSION.READ_EXTERNAL_STORAGE); } const result = await this.androidPermissions.requestPermissions(permissions); console.log('权限请求结果:', result); }
3. 替换 native-storage 插件
native-storage 依赖旧的外部存储路径,在 Android 11+ 直接使用 Capacitor 官方的 @capacitor/preferences 存储键值对,完全不需要权限:
# 安装插件 npm install @capacitor/preferences npx cap sync
使用示例:
import { Preferences } from '@capacitor/preferences'; // 存储数据 await Preferences.set({ key: 'userInfo', value: JSON.stringify({ name: 'Test' }), }); // 获取数据 const { value } = await Preferences.get({ key: 'userInfo' }); const userInfo = JSON.parse(value);
4. 文件下载适配
使用 Capacitor 的 @capacitor/filesystem 插件处理文件,自动兼容分区存储规则:
npm install @capacitor/filesystem npx cap sync
示例:下载文件到公共下载目录(Android 11+ 无需权限)
import { Filesystem, Directory, Encoding } from '@capacitor/filesystem'; async downloadFile() { // 先下载文件到内存 const response = await fetch('https://your-domain.com/file.pdf'); const blob = await response.blob(); const arrayBuffer = await blob.arrayBuffer(); const base64 = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer))); // 写入公共下载目录 await Filesystem.writeFile({ path: 'Download/my-file.pdf', data: base64, directory: Directory.Public, encoding: Encoding.Base64, }); }
如果需要写入非媒体类公共目录,需使用 @capacitor/storage-access-framework 插件让用户手动选择路径。
5. 临时兼容方案(不推荐长期使用)
如果暂时不想大规模修改代码,可以把 android/app/build.gradle 里的 targetSdkVersion 改成30及以下,这样 requestLegacyExternalStorage 就会生效,但这只是权宜之计,Google Play 未来会强制要求 targetSdk 升级到33+,最终还是要适配分区存储:
android { defaultConfig { targetSdkVersion 30 } }
内容的提问来源于stack exchange,提问作者Piyush aggarwal
相关产品推荐
相关产品推荐

