Ionic应用调用Cordova文件插件遇EACCES权限拒绝问题求助
嘿,这个权限拒绝的问题我太熟了!主要是Android 10及以上的存储机制变化,加上旧插件的适配问题导致的,我给你一步步解决:
Android 10(API 29)引入了分区存储(Scoped Storage),限制了直接访问公共存储路径(比如/storage/emulated/0/Download/),而且cordova-plugin-file-transfer和cordova-plugin-file-chooser这类旧插件没完全适配这个变化,再加上权限申请不到位,就会抛出EACCES (Permission denied)。
1. 配置AndroidManifest.xml,兼容旧存储模式
首先在你的项目platforms/android/app/src/main/AndroidManifest.xml里做两个修改:
- 在
<application>标签添加requestLegacyExternalStorage,暂时绕过分区存储限制(过渡方案):
<application ... android:requestLegacyExternalStorage="true">
- 添加必要的存储权限:
<!-- 适配Android 12及以下 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- 适配Android 13+,读取文档类文件需要这个 --> <uses-permission android:name="android.permission.READ_MEDIA_DOCUMENTS" />
2. 主动申请运行时权限
Android 6及以上需要动态申请权限,别指望Manifest配置了就自动生效。用cordova-plugin-android-permissions插件来处理:
先安装插件:
ionic cordova plugin add cordova-plugin-android-permissions npm install @ionic-native/android-permissions
3. 正确处理FileChooser返回的Content URI
fileChooser.open()返回的是content://格式的URI,而旧版file-transfer插件没法直接处理这个,需要用cordova-plugin-file把它转换成真实的文件路径:
修改你的openFile()方法:
import { File } from '@ionic-native/file/ngx'; import { AndroidPermissions } from '@ionic-native/android-permissions/ngx'; import { FileTransfer, FileTransferObject, FileUploadOptions } from '@ionic-native/file-transfer/ngx'; import { FileChooser } from '@ionic-native/file-chooser/ngx'; // ... constructor( private fileChooser: FileChooser, private transfer: FileTransfer, private file: File, private androidPermissions: AndroidPermissions ) {} async openFile() { console.log("openFile"); // 第一步:检查并申请存储权限 const permissionStatus = await this.androidPermissions.checkPermission( this.androidPermissions.PERMISSION.READ_EXTERNAL_STORAGE ); if (!permissionStatus.hasPermission) { const requestResult = await this.androidPermissions.requestPermission( this.androidPermissions.PERMISSION.READ_EXTERNAL_STORAGE ); if (!requestResult.hasPermission) { console.log("用户拒绝了存储权限,无法继续"); return; } } try { // 打开文件选择器 const uri = await this.fileChooser.open(); console.log("选择的文件URI:", uri); // 解析Content URI为FileEntry this.file.resolveLocalFileSystemURL(uri).then(fileEntry => { fileEntry.file(file => { console.log("文件详情:", file); // 配置上传选项 const options: FileUploadOptions = { fileKey: 'file', fileName: file.name, mimeType: file.type, chunkedMode: false, // 这里可以添加其他请求头或参数 }; // 创建上传实例并执行上传 const fileTransfer: FileTransferObject = this.transfer.create(); fileTransfer.upload( fileEntry.nativeURL, '你的后端上传接口地址', options ).then(uploadResult => { console.log("上传成功!", uploadResult); }).catch(uploadError => { console.error("上传失败:", uploadError); }); }); }).catch(resolveError => { console.error("解析文件URI失败:", resolveError); }); } catch (chooserError) { console.error("打开文件选择器失败:", chooserError); } }
4. 长远方案:替换废弃的FileTransfer插件
官方已经标记cordova-plugin-file-transfer为废弃了,推荐用原生的fetch或axios来上传文件,这样更适配新的Android存储机制。比如用fetch实现上传:
async uploadWithFetch(uri: string) { try { // 从Content URI获取Blob const response = await fetch(uri); const blob = await response.blob(); // 构建FormData const formData = new FormData(); // 这里的文件名可以从之前的file对象里获取,或者自己处理 formData.append('file', blob, 'uploaded-file.pdf'); // 发起上传请求 const uploadResponse = await fetch('你的后端上传接口地址', { method: 'POST', body: formData, // 如果需要自定义请求头可以在这里加 headers: { // 'Authorization': 'Bearer token' } }); const result = await uploadResponse.json(); console.log("上传成功:", result); } catch (error) { console.error("上传失败:", error); } }
这样修改后,应该就能解决权限拒绝的问题了。记得测试不同Android版本的设备,尤其是Android 10+和13+的机型。
内容的提问来源于stack exchange,提问作者Jayprakash Singh

