使用自定义文件名写入文件触发FileSystemException权限拒绝异常,常量文件名可正常运行的问题求助
嘿,这个权限问题我之前也碰到过!咱们一步步来排查解决:
先揪出代码里的小笔误
首先看你的方法参数是String name,但代码里却用了customname变量——这肯定是手滑写错了,先把这个改成name,不然变量未定义都跑不起来!
核心问题:权限不足 + 目标文件夹未创建
你看到的FileSystemException: Permission denied, errno = 13,本质是两个原因:要么是目标文件夹不存在,要么是Android存储权限没搞定,尤其是自定义路径的时候这俩问题很常见。
1. 先确保目标文件夹存在
你直接去写$tempDir/myfolder/$name,但如果myfolder这个文件夹还没创建,系统会拒绝写入。所以要先递归创建文件夹:
Future downloadFile(String url, String name) async { final tempDir = (await AndroidExternalStorage.getExternalStoragePublicDirectory( DirType.downloadDirectory)); // 先创建目标文件夹 final folderDir = Directory('$tempDir/myfolder'); await folderDir.create(recursive: true); // recursive: true会自动创建所有父目录 // 再创建文件实例 final file = File('${folderDir.path}/$name'); // ... 后面的代码不变 }
2. 搞定Android存储权限
Android的存储权限规则在不同版本差异很大,分情况处理:
Android 6.0+(API 23):需要动态申请
WRITE_EXTERNAL_STORAGE和READ_EXTERNAL_STORAGE权限。你可以用permission_handler包来简化申请:import 'package:permission_handler/permission_handler.dart'; // 先写一个权限申请方法 Future<bool> checkAndRequestStoragePermission() async { var storageStatus = await Permission.storage.status; if (!storageStatus.isGranted) { storageStatus = await Permission.storage.request(); } return storageStatus.isGranted; }然后在调用
downloadFile前先检查权限:if (await checkAndRequestStoragePermission()) { await downloadFile(yourUrl, yourFileName); } else { // 提示用户去设置里开启存储权限 }Android 10+(API 29):谷歌引入了Scoped Storage,直接访问公共目录需要额外配置。在
AndroidManifest.xml的<application>标签里加一行:<application ... android:requestLegacyExternalStorage="true"> </application>这个配置能让你的应用暂时绕过Scoped Storage的限制,继续用传统方式访问公共下载目录。
Android 11+(API 30):如果你的应用目标SDK是30+,更推荐用
MediaStore来保存文件到公共目录,这种方式不需要申请WRITE权限,更符合新规范:Future<void> saveToDownloads(Uint8List fileBytes, String fileName) async { final contentValues = ContentValues({ MediaStore.Downloads.DISPLAY_NAME: fileName, MediaStore.Downloads.MIME_TYPE: 'application/zip', // 根据你的文件类型修改 MediaStore.Downloads.RELATIVE_PATH: "${Environment.DIRECTORY_DOWNLOADS}/myfolder", }); final uri = await MediaStore.createDocument( context, MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues, ); if (uri != null) { final outputStream = await context.contentResolver.openOutputStream(uri); await outputStream?.write(fileBytes); await outputStream?.close(); } }把Dio获取到的
response.data(字节数据)传给这个方法就行。
最后再验证路径
你可以先打印tempDir的路径,确认是不是/storage/emulated/0/Download——虽然AndroidExternalStorage的方法应该没问题,但有时候不同设备的公共目录可能有小差异,打印出来排查更稳妥。
内容的提问来源于stack exchange,提问作者Azhar Zouhir

