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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 15:35:17