如何在Dart中追踪Multipart数据任务的上传进度?
监听Dart http.MultipartRequest的上传进度
你提到的response.stream.listen确实只能监听响应(下载)的进度,要获取上传进度,需要对请求体的字节流进行包装,在数据发送过程中统计已上传的字节数。
实现步骤
- 计算整个请求的总字节数(包含multipart分隔符、文件数据等所有要上传的内容);
- 包装请求体的原始Stream,在每个数据块发送时累加已上传字节并计算进度;
- 使用包装后的Stream发送请求。
完整代码示例
import 'dart:io'; import 'package:http/http.dart' as http; void main() async { // 准备上传的文件 final file = File('path/to/your/file'); final uploadUrl = Uri.parse('https://your-upload-endpoint.com'); // 创建MultipartRequest var request = http.MultipartRequest('POST', uploadUrl); request.files.add(await http.MultipartFile.fromPath('file', file.path)); // 获取整个请求的总字节数 final totalUploadBytes = await request.contentLength; int uploadedBytes = 0; // 包装原始请求体Stream,添加进度监听 final progressStream = request.finalize().transform(StreamTransformer.fromHandlers( handleData: (dataChunk, sink) { uploadedBytes += dataChunk.length; // 计算进度(0-1之间的浮点数,可转为百分比) final uploadProgress = uploadedBytes / totalUploadBytes; print('上传进度:${(uploadProgress * 100).toStringAsFixed(2)}%'); // 这里可以将进度传递给UI更新,比如通过StatefulWidget的setState // 继续传递数据块,不影响上传流程 sink.add(dataChunk); }, handleError: (error, stackTrace, sink) { sink.addError(error, stackTrace); }, handleDone: (sink) { sink.close(); }, )); // 使用包装后的Stream发送请求 final client = http.Client(); final response = await client.send( http.StreamedRequest(request.method, request.url) ..headers.addAll(request.headers) ..contentLength = totalUploadBytes ..sink.addStream(progressStream) ..sink.close(), ); // 处理响应 final responseBody = await http.Response.fromStream(response); print('上传完成,响应状态码:${response.statusCode}'); print('响应内容:${responseBody.body}'); }
复用工具函数
如果需要在多个地方使用,可以把进度监听逻辑封装成工具函数:
Stream<List<int>> withUploadProgress( Stream<List<int>> originalStream, int totalBytes, void Function(double progress) onProgress, ) { int uploaded = 0; return originalStream.transform(StreamTransformer.fromHandlers( handleData: (data, sink) { uploaded += data.length; final progress = uploaded / totalBytes; onProgress(progress); sink.add(data); }, )); }
使用时只需替换包装Stream的部分:
final progressStream = withUploadProgress( request.finalize(), totalUploadBytes, (progress) { print('上传进度:${(progress * 100).toStringAsFixed(2)}%'); }, );
关键说明
request.finalize()会生成包含所有上传内容的原始字节流,包括multipart协议的分隔符、文件数据、表单字段等;- 通过
StreamTransformer包装流,既能正常传递上传数据,又能在每个数据块发送时统计进度; - 必须使用
StreamedRequest来发送包装后的流,才能保留进度监听逻辑。
内容的提问来源于stack exchange,提问作者Nicolas Degen
相关产品推荐
相关产品推荐

