当你去任一搜索引擎中搜索“Flutter
文件上传”,推送的文章无一不是教你如何用插件实现文件上传。难道说Flutter
不支持文件上传吗?这个答案当然是否定的,插件是Dart
写的,既然插件能实现,Flutter
肯定也能实现。
文件上传有两个必要条件:
Http
请求方法是POST
Content-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; }