Flutter Web从Apache服务端拉取媒体文件的最优实现方案是什么
问题根因
你当前使用ResponseType.bytes的配置,会将整份文件的二进制数据全部加载到Dart虚拟机内存后再调用saveAs导出,当文件体积超过浏览器分配给当前标签页的内存配额时,就会触发内存溢出错误。
推荐解决方案
方案1:流式下载(支持自定义进度、取消逻辑,适配大文件场景)
改用流响应模式,边下载边写入浏览器下载管道,全程不会加载全量文件到内存,内存占用稳定在KB级,可支持GB级文件下载。
修改后代码示例:
import 'package:dio/dio.dart'; import 'package:stream_saver/stream_saver.dart'; // 可直接使用pub上封装好的stream_saver包,或自行通过dart:js_interop对接StreamSaver API // 下载逻辑 Future<void> downloadFile(String medialink, CancelToken cancelToken) async { final response = await dio.get( medialink, cancelToken: cancelToken, onReceiveProgress: (rcv, total) { if (total != -1) { setState(() { progress = ((rcv / total) * 100).toStringAsFixed(0); }); if (rcv == total) { setState(() { isDownloaded = true; downloading = false; }); } } }, options: Options( responseType: ResponseType.stream, // 改为流响应,不加载全量字节 followRedirects: false, validateStatus: (status) => status! < 500, ), ); // 从响应头获取文件名,也可自定义 final contentDisposition = response.headers.value('content-disposition'); String filename = 'download_file'; if (contentDisposition != null) { final matches = RegExp(r'filename="?([^"]+)"?').firstMatch(contentDisposition); if (matches != null) { filename = Uri.decodeFull(matches.group(1)!); } } // 直接将响应流写入下载管道 final writer = createWriteStream(filename).getWriter(); await for (final chunk in response.data.stream) { await writer.write(chunk); } await writer.close(); }
前置配置:
- Apache服务端需要添加响应头配置
Access-Control-Expose-Headers: Content-Length, Content-Disposition,否则进度计算和文件名获取会失效 - 若需要支持断点续传,可在请求头中添加
Range字段,配合Apache默认开启的范围请求能力实现
方案2:原生浏览器下载(无自定义需求时最简实现)
如果不需要自定义进度展示、下载取消等逻辑,直接调用浏览器原生下载能力,完全由浏览器内核处理下载逻辑,不会存在内存溢出问题。
代码示例:
import 'dart:html' as html; void downloadFile(String medialink, String filename) { final anchor = html.AnchorElement(href: medialink); anchor.download = filename; anchor.click(); anchor.remove(); }
内容的提问来源于stack exchange,提问作者Ymc
相关产品推荐
相关产品推荐

