如何控制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
相关产品推荐
相关产品推荐

