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

Flutter应用重装后读取外部文件报FileSystemException权限拒绝错误

问题根因

这个报错和Flutter框架无关,是Android系统原生的存储规则+UID分配机制导致的:

  • Android系统会给每个安装的应用分配独立的Linux UID,应用创建的文件默认仅归属当前UID的进程可读写,哪怕是同包名、同签名的应用,只要UID不同就没有操作权限
  • 首次安装应用时,你写入公共Documents目录的文件归属首次安装的应用UID;卸载重装后,新安装的应用会被分配全新的UID,自然无权读取之前UID创建的残留文件,直接抛出errno=13权限拒绝错误
  • 你硬编码的/storage/emulated/0/Documents/属于公共共享存储路径,在Android 10(API 29)及以上版本受分区存储规则约束,未正确申请权限、未通过系统合规接口访问时,哪怕文件真实存在也会被系统拦截
  • 额外提一句:你代码里写的{"key","value"}不是合法JSON格式,后续做解析会触发格式错误,正确写法是{"key":"value"}
修复方案

根据你的业务需求选对应方案即可:

方案1:使用应用专属外部存储(优先推荐,零权限适配)

如果你的文件不需要给其他应用共享、不需要在应用卸载后保留,完全没必要操作公共存储路径,直接用系统分配的应用专属外部存储目录即可:该目录随应用卸载自动清理,重装不存在UID冲突问题,且不需要申请任何存储权限。

  1. 先在pubspec.yaml中引入官方维护的路径获取插件:
dependencies:
  path_provider: ^2.1.2
  1. 替换你代码中硬编码路径的逻辑:
// 写文件方法
void _writeFile() async {
  // 获取应用专属外部存储路径,无需权限
  final appExtDir = await getExternalStorageDirectory();
  final file = File('${appExtDir?.path}/test2.txt');
  setState(() {
    if (file.existsSync()) {
      _message = 'File already exists';
      _content = '';
    } else {
      const fileContent = '{"key":"value"}';
      file.writeAsStringSync(fileContent);
      _message = 'File created';
      _content = fileContent;
    }
  });
}

// 读文件方法
Future<void> _readFile() async {
  final appExtDir = await getExternalStorageDirectory();
  final file = File('${appExtDir?.path}/test2.txt');
  if (!file.existsSync()) {
    _message = 'File is not there!';
    _content = '';
  } else {
    _message = 'File read :';
    _content = await file.readAsString();
  }
  setState(() {});
}

方案2:需要读写公共存储、访问卸载残留文件的适配

如果业务确实需要读写公共Documents目录、操作卸载后保留的文件,按系统版本做合规适配:

  1. 修改android/app/src/main/AndroidManifest.xml,添加对应权限配置:
<!-- Android 13及以上版本读取媒体文件权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<!-- Android 12及以下版本存储权限,高版本自动失效 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<!-- 仅文件管理类应用申请全存储权限,普通应用申请会被主流应用商店拒审 -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

<application
  android:requestLegacyExternalStorage="true"
  <!-- 其余原有配置保持不变 -->
>
  1. 不要硬编码/storage/emulated/0/开头的绝对路径操作公共文件:Android 10及以上版本访问公共目录文件,优先通过系统存储访问框架(SAF)、MediaStore接口获取文件访问授权,直接硬编码路径会被多数厂商定制ROM拦截。
  2. 针对重装后残留的旧文件:首次启动获取存储权限后,可通过覆盖写入的方式重置文件的UID归属,后续即可正常读写;测试阶段可以手动删除公共目录下的残留旧文件,避免历史文件权限冲突。

补充说明:你看到的报错信息中path显示为空,是系统权限拦截时主动屏蔽了真实路径信息,实际指向的就是你代码中硬编码的test2.txt文件。

内容的提问来源于stack exchange,提问作者Youssef Aly

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 05:09:15