Flutter:如何从Stream返回值或错误?改写下载方法返回字节/错误
解决方案
要让_downloadImage返回最终的字节列表并正确处理错误,你可以通过两种方式实现,同时保留原有的进度更新逻辑:
方法一:使用Completer手动控制Future结果
Completer可以让你在异步回调里手动触发Future的完成或失败,完美适配原来的listen回调模式:
Future<Uint8List> _downloadImage() async { final completer = Completer<Uint8List>(); try { _response = await http.Client() .send(http.Request('GET', Uri.parse('https://upload.wikimedia.org/wikipedia/commons/f/ff/Pizigani_1367_Chart_10MB.jpg'))); _total = _response.contentLength ?? 0; _bytes.clear(); // 每次下载前清空旧数据,避免干扰结果 _response.stream.listen( (value) { setState(() { _bytes.addAll(value); _received += value.length; }); }, onError: (error) { // 流处理出错时标记Future失败 if (!completer.isCompleted) { completer.completeError(error); } }, onDone: () async { try { // 保存文件逻辑不变 final file = File('${(await getApplicationDocumentsDirectory()).path}/image.png'); await file.writeAsBytes(_bytes); setState(() { _image = file; }); // 完成时返回字节列表 if (!completer.isCompleted) { completer.complete(Uint8List.fromList(_bytes)); } } catch (e) { // 保存文件出错也要抛出错误 if (!completer.isCompleted) { completer.completeError(e); } } }, ); } catch (e) { // 请求发送阶段出错直接标记失败 if (!completer.isCompleted) { completer.completeError(e); } } return completer.future; }
方法二:使用await for配合try-catch
如果想用await for,把整个流遍历逻辑放在try-catch块里,就能统一捕获所有阶段的错误:
Future<Uint8List> _downloadImage() async { try { _response = await http.Client() .send(http.Request('GET', Uri.parse('https://upload.wikimedia.org/wikipedia/commons/f/ff/Pizigani_1367_Chart_10MB.jpg'))); _total = _response.contentLength ?? 0; _bytes.clear(); // 遍历流更新进度,流的错误会直接抛出到外层catch await for (final value in _response.stream) { setState(() { _bytes.addAll(value); _received += value.length; }); } // 流遍历完成后保存文件 final file = File('${(await getApplicationDocumentsDirectory()).path}/image.png'); await file.writeAsBytes(_bytes); setState(() { _image = file; }); return Uint8List.fromList(_bytes); } catch (e) { // 统一捕获请求、流处理、文件保存所有阶段的错误 throw e; } }
关键注意点
- 每次下载前清空
_bytes,避免旧数据混入结果 - 两种方式都要覆盖请求发送、流处理、文件保存三个阶段的错误
- 使用
Completer时要检查isCompleted,防止重复触发完成/失败 await for模式下,流内部的错误会直接向外抛出,外层try-catch可以统一处理
内容的提问来源于stack exchange,提问作者May
相关产品推荐
相关产品推荐

