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

Flutter/Dart下载文件报错:卸载后不请求权限且无法创建文件

Flutter 卸载后无法请求存储权限导致文件下载失败的解决方案

问题重现

原本正常运行的Flutter文件下载功能,在安卓模拟器卸载应用后,出现以下错误:

Unhandled Exception: FileSystemException: Cannot create file, path = '/storage/emulated/0/Download/Salary-Sheet.xlsx'

且应用不再主动触发存储权限请求。

核心原因

  • 安卓13(API 33)及以上版本对存储权限做了拆分,原Permission.storage权限不再覆盖公共Download目录的写入权限;
  • 卸载应用后权限完全重置,原代码仅请求Permission.storage,无法满足当前系统的权限要求;
  • 未处理权限请求被拒绝或永久拒绝的场景,导致权限获取失败后直接执行下载逻辑。

解决方案

1. 升级并适配权限请求逻辑

使用最新版permission_handler包,针对安卓13+替换为专门的下载权限:

import 'package:permission_handler/permission_handler.dart';
import 'package:flutter/foundation.dart';

Future<bool> getStoragePermission() async {
  if (defaultTargetPlatform == TargetPlatform.android) {
    final PermissionStatus status = await Permission.downloads.request();
    if (status.isGranted) {
      return true;
    } else if (status.isPermanentlyDenied) {
      // 引导用户前往应用设置开启权限
      await openAppSettings();
      return false;
    } else {
      return false;
    }
  }
  // iOS 保持原有存储权限请求逻辑
  return await Permission.storage.request().isGranted;
}

2. 确保文件目录可写

在下载前检查并创建目标目录,避免因目录不存在导致创建文件失败:

downloaddd(String downloadPath) async {
  var path = '$downloadPath/Salary-Sheet.xlsx';
  final Directory directory = Directory(downloadPath);
  
  // 递归创建目录(包含所有父级目录)
  if (!await directory.exists()) {
    await directory.create(recursive: true);
  }

  String url = "http://salary/export/${widget.masterID}";

  try {
    await dio.download(url, path, onReceiveProgress: (received, total) {
      if (total != -1) {
        Navigator.pop(context);
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            behavior: SnackBarBehavior.floating,
            content: Row(
              children: const [
                Icon(Icons.cloud_download),
                SizedBox(width: 10),
                Text("Salary sheet has been downloaded !"),
              ],
            ),
          ),
        );
      }
    });
  } catch (e) {
    // 捕获并提示下载错误
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text("下载失败: ${e.toString()}")),
    );
  }
}

3. 配置AndroidManifest.xml权限

根据安卓版本添加对应权限声明:

<!-- 安卓12及以下版本需要的存储权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />

<!-- 安卓13+访问Download目录的权限 -->
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />

<!-- 如需全存储访问(需Google Play审核说明用途) -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

4. 可选:使用应用私有存储

如果不需要文件在系统公共Download目录显示,可改用应用私有存储,无需申请存储权限:

import 'package:path_provider/path_provider.dart';

Future<String> getDownloadPath() async {
  // 获取应用外部私有存储目录
  final Directory? externalDir = await getExternalStorageDirectory();
  return "${externalDir!.path}/Download";
}

内容的提问来源于stack exchange,提问作者swapnil mane

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 04:45:35