当你去任一搜索引擎中搜索“Flutter文件上传”,推送的文章无一不是教你如何用插件实现文件上传。难道说Flutter不支持文件上传吗?这个答案当然是否定的,插件是Dart写的,既然插件能实现,Flutter肯定也能实现。
文件上传有两个必要条件:
Http请求方法是POSTContent-Type必须是FormData
关键问题就在于Flutter没有提供FormData相关的接口,因此需要我们手写Http报文来实现文件上传。
首先分析一下报文格式,假设某个接口接受form-data格式的title字段和image文件字段,那么对应的核心报文如下:
... Content-Type:multipart/form-data; boundary=custom-boundary ... --custom-boundary Content-Disposition:form-data; name=title imageTitle --custom-boundary Content-Disposition:form-data; name=image; filename=image.png [bindary content of image.png] --custom-boundary--
请求头中需要设置Content-Type为multipart/form-data,并自定义一个分隔符custom-boundary(分隔符越复杂越好)。请求体中用--[自定义分隔符]来对每个字段进行分隔,设置Content-Disposition为form-data,name为字段名,在两个换行之后输入字段值,其他字段按照同样的规则追加到新的一行,最终以--[自定义分隔符]--结束请求体。
实现代码:
Future<T> send<T> (final String method, final String host, final String path, {
final Map<String, String>? para,
final Map<String, dynamic>? data,
}) async {
/// http请求对象
final request = await HttpClient().openUrl(method, Uri.http(host, path, para));
if (method != 'get' && data != null) {
/// 请求方法不是get时,设置http协议类型为form-data,并自定义字段分隔符boundary
final boundary = 'abcdefghijklmnopqrstuvwxyz';
request.headers.contentType = ContentType('multipart', 'form-data', parameters: {
'boundary': boundary,
});
/// 分别将字段写入http报文
for (final key in data.keys) {
final value = data[key];
if (value is File) {
/// 写入文件字段
request.write('\n--$boundary\n');
request.write('Content-Disposition: form-data; name=$key; filename=${value.path}\n\n');
request.write('${String.fromCharCodes(value.readAsBytesSync())}\n');
} else {
/// 写入其他字段
request.write('\n--$boundary\n');
request.write('Content-Disposition: form-data; name=$key\n\n');
request.write('$value\n');
}
}
/// 写入http报文结尾
request.write('--$boundary--');
}
/// 获取服务器响应
final response = await request.close();
/// 转为utf8编码的json字符串
final json = await response.transform(Utf8Decoder()).join();
/// 转为指定类型
final res = jsonDecode(json) as T;
/// 返回
return res;
}