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

如何使用http包实现类似Dio的onSendProgress文件上传进度监听

在Flutter http包中实现上传进度监听

要在http包的Multipart上传中实现类似Dio的onSendProgress功能,核心思路是包装文件的ByteStream,在数据流传输时统计已发送字节数,具体实现步骤如下:

1. 实现进度跟踪的Stream包装器

先写一个工具函数,用来包装原始的文件字节流,在每段数据发送时触发进度回调:

Stream<List<int>> trackUploadProgress(
  Stream<List<int>> stream,
  int totalLength,
  Function(int sent, int total) onProgress,
) {
  int sentBytes = 0;
  return stream.map((chunk) {
    sentBytes += chunk.length;
    onProgress(sentBytes, totalLength);
    return chunk;
  });
}

2. 计算上传总数据量

要展示整体上传进度,需要先计算所有表单字段和文件的总大小:

  • 表单字段的大小:把每个字段的key和value转成字节后(包含multipart表单的分隔符、头部信息)求和
  • 文件的总大小:所有选中文件的长度之和
// 计算表单字段的总字节数(近似值)
int calculateFieldsTotalSize(http.MultipartRequest request) {
  int total = 0;
  request.fields.forEach((key, value) {
    total += '--${request.boundary}\r\nContent-Disposition: form-data; name="$key"\r\n\r\n$value\r\n'.length;
  });
  return total;
}

// 计算所有文件的总字节数
int calculateFilesTotalSize(List<File> files) {
  return files.fold(0, (sum, file) => sum + file.lengthSync());
}

3. 修改原上传代码,集成进度监听

在创建MultipartFile时,替换原始的ByteStream为带进度跟踪的流,同时维护全局的已发送字节数和总大小:

// 全局变量跟踪总进度
int _totalUploadSize = 0;
int _sentBytes = 0;

function() async{
  try{
    var uri = Uri.parse('MY API');
    http.MultipartRequest request = http.MultipartRequest('POST', uri);

    request.fields['subject'] = newDemandSubjectController.text;
    request.fields['text'] = newDemandBodyController.text;

    List<http.MultipartFile> newList = <http.MultipartFile>[];

    // 计算总上传大小:表单字段 + 文件总大小 + 表单结尾分隔符
    int fieldsSize = calculateFieldsTotalSize(request);
    int filesSize = calculateFilesTotalSize(selectedImages.map((img) => File(img.path)).toList());
    _totalUploadSize = fieldsSize + filesSize + '--${request.boundary}--\r\n'.length;
    _sentBytes = fieldsSize; // 表单字段会先被发送,初始值设为字段总大小

    for(var image in selectedImages){
      File imageFile =  File(image.path);
      var stream = imageFile.openRead();
      var fileLength = imageFile.lengthSync();
      
      // 包装流,跟踪单个文件的上传进度,并累加全局进度
      var progressStream = trackUploadProgress(
        stream,
        fileLength,
        (sent, total) {
          _sentBytes = fieldsSize + sent;
          // 这里可以触发UI更新,比如通知进度条组件
          print('上传进度:${(_sentBytes / _totalUploadSize * 100).toStringAsFixed(2)}%');
          print('已发送:$_sentBytes / 总大小:$_totalUploadSize');
        },
      );

      var multipartFile = http.MultipartFile(
        "images[]", 
        http.ByteStream(progressStream), 
        fileLength,
        filename: '${DateTime.now()}.${image.path.split('.').last}'
      );
      newList.add(multipartFile);
    }

    request.headers.assignAll({
      'Accept': 'application/json',
      'Authorization': 'Bearer $USER_TOKEN'
    });

    request.files.addAll(newList);

    var response = await request.send();

    response.stream.transform(utf8.decoder).listen((value) {
      print(value);
    });

    if (response.statusCode == 200) {
      print('上传完成');
    } else {
      print('上传失败');
    }

  }catch(e){
    return Future.error(jsonDecode(e.toString()));
  }
}

注意事项

  • 表单字段的大小计算是近似值,实际multipart表单的分隔符、头部信息会有字节占用,这个计算能保证进度条大致准确;若需要更精确可以直接解析request的完整字节流,但复杂度会更高。
  • 若要在UI中更新进度条,建议使用ValueNotifier或状态管理工具(如Provider、Riverpod)来通知UI刷新,避免直接在回调中操作UI。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 20:22:29