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

重试401请求时如何每次创建新的MultipartFile?

解决MultipartFile重试时Stream已耗尽的问题

核心问题是MultipartFile的stream属于单订阅流,第一次请求发送后就会被耗尽,重试时无法再次读取。以下是两种可行的解决方案:

方案一:自定义可复用的MultipartFile(适合小文件)

通过自定义MultipartFile子类,让每次获取stream时都返回新的字节流,支持重复读取:

import 'dart:typed_data';
import 'package:http/http.dart' as http;

class ReusableBytesMultipartFile extends http.MultipartFile {
  final List<int> _fileBytes;

  ReusableBytesMultipartFile(
    String field,
    this._fileBytes, {
    String? filename,
    http.MediaType? contentType,
  }) : super(
          field,
          Stream.value(_fileBytes),
          _fileBytes.length,
          filename: filename,
          contentType: contentType,
        );

  // 每次获取stream都返回新的字节流,支持重复读取
  @override
  Stream<List<int>> get stream => Stream.value(_fileBytes);
}

使用方式

// 先把文件读取到内存
final fileBytes = await File('path/to/your/file').readAsBytes();
final multipartFile = ReusableBytesMultipartFile(
  'file',
  fileBytes,
  filename: 'example.jpg',
  contentType: http.MediaType('image', 'jpeg'),
);

// 构建请求
final request = http.MultipartRequest('POST', Uri.parse('your-api-endpoint'))
  ..files.add(multipartFile)
  ..fields.addAll({'key': 'value'});

方案二:封装请求生成器(适合大文件,避免内存占用过高)

通过保存请求的原始参数(如文件路径),每次重试时重新构建MultipartRequest和MultipartFile:

import 'package:http/http.dart' as http;

class MultipartRequestTemplate {
  final String method;
  final Uri uri;
  final Map<String, String> baseHeaders;
  final Map<String, String> fields;
  final List<({String fieldName, String filePath, String? filename, http.MediaType? contentType})> fileInfos;

  MultipartRequestTemplate({
    required this.method,
    required this.uri,
    required this.baseHeaders,
    required this.fields,
    required this.fileInfos,
  });

  // 每次调用都生成全新的MultipartRequest和MultipartFile
  Future<http.MultipartRequest> buildRequest() async {
    final request = http.MultipartRequest(method, uri)
      ..headers.addAll(baseHeaders)
      ..fields.addAll(fields);

    for (final info in fileInfos) {
      final file = await http.MultipartFile.fromPath(
        info.fieldName,
        info.filePath,
        filename: info.filename,
        contentType: info.contentType,
      );
      request.files.add(file);
    }

    return request;
  }
}

使用方式

// 创建请求模板
final requestTemplate = MultipartRequestTemplate(
  method: 'POST',
  uri: Uri.parse('your-api-endpoint'),
  baseHeaders: {'Content-Type': 'multipart/form-data'},
  fields: {'description': 'upload test'},
  fileInfos: [
    (
      fieldName: 'file',
      filePath: 'path/to/large-file.zip',
      filename: 'large-file.zip',
      contentType: http.MediaType('application', 'zip'),
    ),
  ],
);

// 发起请求时调用buildRequest()生成新请求
final request = await requestTemplate.buildRequest();
final response = await client.send(request);

注意事项

  • 方案一适合小文件,大文件会占用过多内存;
  • 方案二每次重试都会重新读取文件,对磁盘IO有一定开销,但内存友好;
  • 确保你的restoreAccessToken方法是线程安全的,避免多次重试时重复发起token刷新请求。

内容的提问来源于stack exchange,提问作者Santosh Kumar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 04:40:23