Flutter Web使用Dio上传视频到AWS S3时如何显示上传进度
问题核心原因
- 你调用了
MultipartFile.fromBytes(imageBytes).finalize(),直接把文件转成了字节流,Dio无法获知总文件大小,onSendProgress的totalBytes会返回-1,导致进度计算失效 - Flutter Web端默认使用的XHR适配器对
onSendProgress的支持有特殊要求,必须明确传递请求的Content-Length头,且不能提前finalize文件 - 你设置了
contentType: "multipart/form-data",但上传到S3的预签名PUT请求不需要form-data格式,直接传二进制即可,错误的content-type也会干扰进度监听
修复后的实现代码
uploadFile(String url, Uint8List videoBytes) async { BaseOptions options = BaseOptions( // 直接用二进制content-type,不要用multipart/form-data,S3预签名PUT不需要表单格式 contentType: "video/mp4", // 可以根据实际视频格式调整,或者传application/octet-stream headers: { 'Accept': "*/*", 'Content-Length': videoBytes.length.toString(), // 显式传内容长度,Web端必须 }, connectTimeout: 200000, receiveTimeout: 200000, sendTimeout: 200000, followRedirects: true, validateStatus: (status) { print('Status: $status'); return status! <= 500; }); Dio _dio = Dio(options); // 不要调用finalize,直接传字节流即可,Dio会自动统计发送进度 await _dio.put( url, data: Stream.fromIterable(videoBytes.map((e) => [e])), options: Options( headers: { Headers.contentLengthHeader: videoBytes.length, // 再明确指定一次content-length,避免被Dio覆盖 }, ), onSendProgress: (int sentBytes, int totalBytes) { // 先判断totalBytes是否有效,避免除以0或者负数报错 if (totalBytes <= 0) return; double progressPercent = sentBytes / totalBytes * 100; print("上传进度: ${progressPercent.toStringAsFixed(2)} %"); if (progressPercent >= 100) { dispose(); } }, ).then((value) { print("上传成功,响应: ${value.data}"); }).catchError((onError) { print("上传出错: $onError"); dispose(); }); }
额外注意事项
- 如果你的视频体积超过100M,建议使用S3的分片上传接口,单文件上传的进度监听在大文件场景下容易因为网络波动中断,分片上传可以实现断点续传,进度统计也更稳定
- 不要在请求头里加
Access-Control-Allow-*相关配置,这些是服务端返回响应时携带的头,客户端请求带了不会生效,反而可能触发S3的跨域校验失败 - 如果还是拿不到进度,可以检查S3的CORS配置,是否允许
PUT请求,是否暴露了Content-Length头
内容的提问来源于stack exchange,提问作者Pratik Butani
相关产品推荐
相关产品推荐

