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

Flutter从HTTP POST响应下载Excel文件的问题求助

解决Flutter POST请求下载Excel的两个问题

问题1:请求响应为null的原因及修复

你的代码存在两处关键冲突,导致请求返回null:

  1. 全局配置与接口返回类型不匹配:全局BaseOptions设置了responseType: ResponseType.json,同时headers指定Accept: application/json,但接口实际返回的是Excel二进制文件(Content-Type为application/vnd.openxmlformats-officedocument.spreadsheetml.sheet),Dio会尝试把二进制内容解析成JSON,直接导致解析失败或返回null。
  2. Accept头限制了响应类型:全局Accept头只接收JSON,服务器返回的文件类型被拒绝,无法正常接收响应。

修复后的代码

Dio? http;

// 初始化时移除全局的JSON响应类型限制
BaseOptions options = BaseOptions(
  baseUrl: "****/",
  headers: {
    'Content-type': 'application/json'
  },
);

http = Dio(options);

static Future<void> getClientsFile(String sheetName) async {
  Directory dir = await getApplicationDocumentsDirectory();
  String fileLocation = dir.path + "/" + sheetName + ".xlsx";

  try {
    final response = await http!.download(
      "clients/exports",
      fileLocation,
      data: {"sheetName": sheetName, "type": "xlsx"},
      options: Options(
        method: "POST",
        responseType: ResponseType.bytes,
        // 覆盖全局Accept头,允许接收任意类型内容
        headers: {'Accept': '*/*'}
      ),
    );
    print("文件保存成功,路径:$fileLocation");
    print("响应状态码:${response.statusCode}");
  } catch (e) {
    print("请求错误:$e");
  }
}

// 调用
await ClientController.getClientsFile(sheetName);

如果Dio的download方法对POST请求的data支持存在版本问题,可改用post方法手动处理文件保存:

static Future<void> getClientsFile(String sheetName) async {
  Directory dir = await getApplicationDocumentsDirectory();
  String fileLocation = dir.path + "/" + sheetName + ".xlsx";

  try {
    final response = await http!.post(
      "clients/exports",
      data: {"sheetName": sheetName, "type": "xlsx"},
      options: Options(
        responseType: ResponseType.bytes,
        headers: {'Accept': '*/*'}
      ),
    );

    if (response.statusCode == 200) {
      File file = File(fileLocation);
      await file.writeAsBytes(response.data);
      print("文件保存成功:$fileLocation");
    } else {
      print("请求失败,状态码:${response.statusCode}");
    }
  } catch (e) {
    print("请求错误:$e");
  }
}

问题2:保存到用户指定位置

当前代码使用getApplicationDocumentsDirectory保存到APP私有目录,用户无法直接访问。若要让用户指定保存位置,可借助file_picker库实现目录选择:

  1. 添加依赖到pubspec.yaml:
dependencies:
  file_picker: ^5.0.0
  1. 修改代码实现用户自选路径:
static Future<void> getClientsFile(String sheetName) async {
  // 让用户选择保存目录
  String? selectedDirectory = await FilePicker.platform.getDirectoryPath();
  if (selectedDirectory == null) {
    print("用户取消了目录选择");
    return;
  }

  String fileLocation = "$selectedDirectory/$sheetName.xlsx";

  try {
    final response = await http!.post(
      "clients/exports",
      data: {"sheetName": sheetName, "type": "xlsx"},
      options: Options(
        responseType: ResponseType.bytes,
        headers: {'Accept': '*/*'}
      ),
    );

    if (response.statusCode == 200) {
      File file = File(fileLocation);
      await file.writeAsBytes(response.data);
      print("文件已保存到:$fileLocation");
    }
  } catch (e) {
    print("错误:$e");
  }
}

移动端权限注意事项

  • Android:在AndroidManifest.xml中添加读写权限(Android 13+需添加READ_MEDIA_IMAGES等细分权限,或申请MANAGE_EXTERNAL_STORAGE权限)。
  • iOS:在Info.plist中添加NSDocumentFolderUsageDescription字段,说明访问文件目录的用途。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 00:43:11