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

如何控制StreamedResponse数据流,获取完整JSON数组分片?

问题

我计划调用API,希望在数据下载过程中将其映射到对应类中。但使用StreamedResponse时,返回的数据流会被拆分为多个分片,且可能在JSON数组的对象中间截断。例如完整JSON为包含两个对象的数组,流可能拆分为截断了第一个对象的分片,以及包含剩余内容的分片。我希望配置或控制数据流,让每个分片包含完整的数组元素(如第一个分片包含第一个完整对象,第二个分片包含第二个对象及数组结尾),且不想累积全部数据流,而是逐个处理分片。

当前使用的代码示例如下:

final client = http.Client();
final request  = http.Request("GET", Uri.parse(url));
Future<http.StreamedResponse> response = client.send(request);

response.asStream().listen((streamResponse){
  streamResponse.stream.listen((data) {
    String responseData = utf8.decode(data);
    print('Received $responseData');
  });
});
解决方案

要实现逐个处理完整的JSON数组元素,你需要对数据流做分块JSON解析——不能直接处理原始字节分片,因为这些分片不保证JSON结构的完整性。以下是两种可行的实现方式:

方法一:使用JsonDecoder的原生流解析能力

Dart的dart:convert库自带的JsonDecoder可以直接处理字节流,自动拼接分片并输出完整的JSON对象,无需手动维护缓冲区:

import 'dart:convert';
import 'package:http/http.dart' as http;

void fetchAndProcessStream(String url) async {
  final client = http.Client();
  final request = http.Request("GET", Uri.parse(url));
  final streamedResponse = await client.send(request);

  // 解析字节流,将整个JSON数组拆分为单个元素输出
  final decoder = JsonDecoder();
  final itemStream = decoder.bind(streamedResponse.stream)
      .cast<List<dynamic>>()
      .expand((array) => array);

  // 逐个处理完整的数组元素
  await for (final item in itemStream) {
    print('处理完整对象: $item');
    // 这里直接映射到你的实体类,比如:
    // final myObject = MyClass.fromJson(item);
  }

  client.close();
}

这个方法会自动处理所有分片截断的情况,是最简洁可靠的方案。

方法二:手动维护缓冲区(适合自定义解析逻辑)

如果需要更精细的控制(比如自定义分隔符、处理特殊格式),可以手动维护字符串缓冲区,每次收到新分片后追加,再查找完整的JSON对象边界:

import 'dart:convert';
import 'package:http/http.dart' as http;

void fetchAndProcessStreamManually(String url) async {
  final client = http.Client();
  final request = http.Request("GET", Uri.parse(url));
  final streamedResponse = await client.send(request);

  String buffer = '';
  bool inArray = false;

  await for (final chunk in streamedResponse.stream) {
    buffer += utf8.decode(chunk);

    // 跳过数组开头的[
    if (!inArray) {
      final bracketIndex = buffer.indexOf('[');
      if (bracketIndex != -1) {
        buffer = buffer.substring(bracketIndex + 1);
        inArray = true;
      } else {
        continue;
      }
    }

    // 循环提取完整的JSON对象
    while (true) {
      // 先检查是否到数组结尾
      final endBracketIndex = buffer.indexOf(']');
      if (endBracketIndex != -1) {
        final lastItemStr = buffer.substring(0, endBracketIndex).trim();
        if (lastItemStr.isNotEmpty && lastItemStr != ',') {
          final item = jsonDecode(lastItemStr);
          print('处理最后一个完整对象: $item');
        }
        buffer = '';
        break;
      }

      // 查找对象分隔符,
      final commaIndex = buffer.indexOf(',');
      if (commaIndex != -1) {
        final itemStr = buffer.substring(0, commaIndex).trim();
        if (itemStr.isNotEmpty) {
          final item = jsonDecode(itemStr);
          print('处理完整对象: $item');
        }
        buffer = buffer.substring(commaIndex + 1);
      } else {
        // 当前缓冲区没有完整对象,等待下一个分片
        break;
      }
    }
  }

  client.close();
}

注意:这种方法需要处理JSON内部的特殊字符(比如字符串里的,),结构复杂时推荐用方法一。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 21:04:53