import 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; import 'package:dio/dio.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:logger/logger.dart'; import '../config/env.dart'; import '../providers/app_store.dart'; ///封装流式请求 class StreamUtils { var logger = Logger(); Dio dio = Dio(); ///发送流式请求 /// - [url] 请求的URL /// - [data] 请求的数据,默认为空 /// - [onCall] 可选的回调函数,用于处理每个数据块 /// - [onEnd] 可选的回调函数,用于处理请求结束 /// - [onError] 可选的回调函数,用于处理请求错误 Future sendStream( String url, { Map data = const {}, Function(Map chunk)? onCall, Function()? onEnd, Function()? onError, }) async { String token = await AppStore.getToken(); Response response = await dio.post( "${Config.baseUrl()}$url", data: data, options: Options( responseType: ResponseType.stream, headers: { 'Content-Type': 'application/json', 'Authorization': "Bearer $token", }, ), ); if (await _isValidResponse(response)) { onError?.call(); return; } //数据 var bufferText = ""; //吐出来的内容 //处理响应 response.data?.stream .transform(_unit8Transformer()) .transform(utf8.decoder) .listen( (chunk) { List chunkList = chunk.split("\n"); for (var element in chunkList) { String streamStr = element.replaceAll("data: ", '').trim(); try { if (streamStr.isNotEmpty) { if (streamStr == '[DONE]') { // 流结束标志,忽略不处理 return; } Map dataJSON = jsonDecode(streamStr); //提取响应数据 Map choices = dataJSON['choices'][0]; //提取文字 var word = choices['delta']['content']; //回调 if (word != null) { bufferText += word; } onCall?.call({...choices, "text": bufferText}); } } catch (e) { onError?.call(); logger.e("流错误响应: $e"); } } }, onDone: () { onEnd?.call(); }, onError: (error) { onError?.call(); logger.e("流错误: $error"); }, ); } /// 将Uint8List转换为List StreamTransformer> _unit8Transformer() { StreamTransformer> unit8Transformer = StreamTransformer.fromHandlers( handleData: (data, sink) { sink.add(List.from(data)); }, ); return unit8Transformer; } // 判断是否是正常请求 Future _isValidResponse(Response response) async { final contentType = response.headers.value('content-type') ?? ''; if (contentType.contains('application/json')) { final data = response.data; if (data is ResponseBody) { // 把流里的所有字节收集起来 final bytes = await data.stream.fold>( [], (previous, element) => previous..addAll(element), ); var res = jsonDecode(utf8.decode(bytes)); if (res['code'] == 0) { EasyLoading.showToast(res['message']); } } return true; } return false; } }