Flutter解密下载文件时遇Permission denied(errno=13)权限问题求助
问题排查与解决方案
核心问题分析
报错Permission denied, errno = 13本质是运行时未实际获取到文件读写权限——虽然你在AndroidManifest.xml中声明了权限,但Android 6.0(API 23)及以上系统要求危险权限必须动态申请,仅清单声明不足以生效。另外你的下载逻辑用固定延迟等待文件写入,存在文件未完全保存就执行解密的潜在风险,也可能触发类似报错。
分步解决
1. 动态申请文件读写权限
使用permission_handler包处理动态权限请求:
- 先在
pubspec.yaml添加依赖:
dependencies: permission_handler: ^10.2.0
- 在执行下载和解密前,先完成权限校验:
import 'package:permission_handler/permission_handler.dart'; // 新增权限检查函数 Future<bool> checkAndRequestStoragePermissions() async { var status = await Permission.storage.status; if (!status.isGranted) { final result = await Permission.storage.request(); return result.isGranted; } return true; } // 修改按钮点击逻辑 onPressed: () async { print("Checking permissions..."); bool hasPermission = await checkAndRequestStoragePermissions(); if (!hasPermission) { print("Storage permission denied"); return; } print("Downloading file from drive..."); await downloadFromDrive("ideas", fileId.toString()); print("Decrypting the file downloaded from drive..."); await decryptJsonFile( srcPath: '/storage/emulated/0/Download/test_gdrive_download.txt.aes', destPath: '/storage/emulated/0/Download/decrypted_file.txt'); },
2. 修复下载逻辑的异步等待问题
替换不可靠的固定延迟,用Completer监听文件流写入完成:
Future<void> downloadFromDrive(String fName, String gdID) async { var client = GoogleAuthClient(authHeaders); var ga = drive.DriveApi(client); drive.Media file = await ga.files .get(gdID, downloadOptions: drive.DownloadOptions.fullMedia) as drive.Media; final saveFile = File('/storage/emulated/0/Download/test_gdrive_download.txt.aes'); List<int> dataStore = []; Completer<void> completer = Completer<void>(); file.stream.listen((data) { print("DataReceived: ${data.length}"); dataStore.addAll(data); // 用addAll替代insertAll,更高效 }, onDone: () async { print("Task Done"); await saveFile.writeAsBytes(dataStore); print("File saved at ${saveFile.path}"); completer.complete(); }, onError: (error) { print("Some Error: $error"); completer.completeError(error); }); return completer.future; // 等待文件流真正写入完成 }
3. 适配Android 11+的存储权限(可选)
若应用目标SDK为30+(Android 11),requestLegacyExternalStorage仅对Android 10生效,需额外处理:
- 若必须访问公共Download目录,添加
MANAGE_EXTERNAL_STORAGE权限(需在Google Play审核时说明用途):
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
- 更新权限检查逻辑:
import 'package:device_info_plus/device_info_plus.dart'; Future<bool> checkAndRequestStoragePermissions() async { if (Platform.isAndroid) { final androidInfo = await DeviceInfoPlugin().androidInfo; if (androidInfo.version.sdkInt >= 30) { var status = await Permission.manageExternalStorage.status; if (!status.isGranted) { final result = await Permission.manageExternalStorage.request(); return result.isGranted; } } else { var status = await Permission.storage.status; if (!status.isGranted) { final result = await Permission.storage.request(); return result.isGranted; } } } return true; }
4. 前置验证文件状态
解密前先确认源文件存在且可读取:
decryptJsonFile({required String srcPath, required String destPath}) async { File srcFile = File(srcPath); if (!await srcFile.exists()) { throw Exception("Source file does not exist"); } if (!await srcFile.readable()) { throw Exception("No permission to read source file"); } crypt.decryptFileSync(srcPath, destPath); }
总结
优先解决动态权限申请和下载逻辑的异步等待问题,这是触发当前报错的核心原因。针对更高版本Android系统,再按需适配存储权限策略。
内容的提问来源于stack exchange,提问作者Kavishka Rajapakshe
相关产品推荐
相关产品推荐

