重试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
相关产品推荐
相关产品推荐

