Flutter从HTTP POST响应下载Excel文件的问题求助
解决Flutter POST请求下载Excel的两个问题
问题1:请求响应为null的原因及修复
你的代码存在两处关键冲突,导致请求返回null:
- 全局配置与接口返回类型不匹配:全局
BaseOptions设置了responseType: ResponseType.json,同时headers指定Accept: application/json,但接口实际返回的是Excel二进制文件(Content-Type为application/vnd.openxmlformats-officedocument.spreadsheetml.sheet),Dio会尝试把二进制内容解析成JSON,直接导致解析失败或返回null。 - 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库实现目录选择:
- 添加依赖到
pubspec.yaml:
dependencies: file_picker: ^5.0.0
- 修改代码实现用户自选路径:
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
相关产品推荐
相关产品推荐

