Flutter写入非临时文件报Operation not permitted,如何写入用户可访问的外部文件
Flutter Android 外部路径写入失败问题解决方案
根因分析
- Android 10(API 29)及以上系统默认启用分区存储机制,传统的
WRITE_EXTERNAL_STORAGE权限已被废弃,即便申请通过也无法直接写入外部存储的任意自定义路径,你收到的Operation not permitted, errno = 1报错就是该限制导致的。 - 代码中
writeAsBytes方法未添加await修饰,属于隐性异步漏洞,会导致文件写入流程未执行完成就进入下一轮循环,可能出现文件不完整、写入逻辑混乱的问题。 - 路径拼接直接使用字符串相加,未适配不同平台的路径分隔符规则,可能出现路径格式错误。
修复方案
1. 权限配置适配
修改AndroidManifest.xml配置,按系统版本适配权限:
<!-- 适配Android 9及以下版本的存储权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <!-- Android 10版本启用旧存储逻辑过渡,Android 11及以上版本该配置无效 --> <application android:requestLegacyExternalStorage="true" ... 其他application配置 > </application>
如果你的应用需要适配Android 11(API 30)及以上版本的任意路径写入需求,还需要添加全文件管理权限(注意:该权限上架Google Play需要提供合理的使用场景说明,否则会被驳回):
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
2. 代码逻辑修正
首先在pubspec.yaml中添加依赖:
dependencies: path: ^1.8.3 device_info_plus: ^9.1.1 permission_handler: ^10.4.3
修改业务代码适配权限和异步逻辑:
import 'dart:io'; import 'package:path/path.dart' as path; import 'package:device_info_plus/device_info_plus.dart'; import 'package:permission_handler/permission_handler.dart'; Directory? dir; // 按Android系统版本申请对应权限 if (Platform.isAndroid) { final androidInfo = await DeviceInfoPlugin().androidInfo; if (androidInfo.version.sdkInt >= 30) { await Permission.manageExternalStorage.request(); if (!await Permission.manageExternalStorage.isGranted) return; } else { await Permission.storage.request(); if (!await Permission.storage.isGranted) return; } } String? dirPath = await FilePicker.platform.getDirectoryPath(); if (dirPath == null) return; dir = await Directory(dirPath).create(recursive: true); for (var pdf in selected) { var bytes = base64Decode(pdf.pdfData.replaceAll('\n', '')); // 用path包拼接路径,自动适配平台分隔符 final saveFilePath = path.join(dir.path, 'Receipt-${DateTime.now().millisecondsSinceEpoch}.pdf'); File savedFile = await File(saveFilePath).create(recursive: true); // 添加await等待文件写入完成 await savedFile.writeAsBytes(bytes.buffer.asUint8List()); }
3. 合规推荐方案(无需申请特殊权限)
如果不需要自定义存储路径,推荐将文件写入系统公共分类目录(如Download、Documents目录),该方案完全适配分区存储规则,无需申请全文件管理权限,用户也可以直接在系统文件管理器中访问到导出的文件:
首先添加path_provider依赖:
dependencies: path_provider: ^2.1.0
代码实现:
// 获取系统公共下载目录 final? publicDownloadDir = await getExternalStoragePublicDirectory(DirectoryDownloads); if (publicDownloadDir == null) return; // 在下载目录下创建专属的Receipts文件夹 final targetDir = await Directory(path.join(publicDownloadDir.path, 'Receipts')).create(recursive: true); // 后续写入逻辑和上面一致
内容的提问来源于stack exchange,提问作者Braden Coates
相关产品推荐
相关产品推荐

