Flutter不依赖插件实现文件上传

当你去任一搜索引擎中搜索“Flutter文件上传”,推送的文章无一不是教你如何用插件实现文件上传。难道说Flutter不支持文件上传吗?这个答案当然是否定的,插件是Dart写的,既然插件能实现,Flutter肯定也能实现。

文件上传有两个必要条件:

  1. Http请求方法是POST
  2. 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-Typemultipart/form-data,并自定义一个分隔符custom-boundary(分隔符越复杂越好)。请求体中用--[自定义分隔符]来对每个字段进行分隔,设置Content-Dispositionform-dataname为字段名,在两个换行之后输入字段值,其他字段按照同样的规则追加到新的一行,最终以--[自定义分隔符]--结束请求体。

实现代码:

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;
}

留下评论

邮箱地址不会被公开。 必填项已用*标注

给博主打赏

2元 5元 10元