You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用自定义文件名写入文件触发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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 17:57:30