Flutter中如何发送携带JSON对象、图片的multipart/form-data POST请求
Dart 环境下携带嵌套JSON对象+图片的multipart POST实现
关键前置修正
注意:你原配置里有个会直接导致请求失败的问题:不要手动在请求头里声明Content-Type: multipart/form-data。
multipart/form-data 格式要求每个请求分段之间携带随机生成的boundary边界标识,手动写死的Content-Type会缺失这个标识,服务端会直接无法解析请求结构。这个头必须由网络客户端在构建请求体时自动生成,你只需要在请求头里保留自定义的认证和租户参数即可,修正后的请求头配置:
final Map<String, String> headers = { 'tenantId': tenantId, 'x-access-token': token, };
核心实现逻辑
由于multipart/form-data 原生只支持扁平键值对格式,无法直接承载你定义的嵌套结构化对象,标准处理方案是:
- 把整个嵌套的业务数据(也就是你写的包含
Id、嵌套Details结构的对象)序列化成合法JSON字符串,作为普通表单字段提交 - 把图片封装成二进制文件表单字段,和JSON数据字段一起放到multipart请求体里
先准备序列化的业务数据:
import 'dart:convert'; // 构建你的业务数据对象 final businessPayload = { "Id": Id, "Details": { "name": Name, "centroid": centroid, "attribute": { "boundaryOpacity": boundaryOpacity, "boundaryWeight": boundaryWeight, "boundaryColor": boundaryColor, "labelColor": labelColor, }, } }; // 序列化为JSON字符串 final businessPayloadJson = jsonEncode(businessPayload);
方案1:使用官方http包实现
import 'package:http/http.dart' as http; // 初始化multipart POST请求 final request = http.MultipartRequest( 'POST', Uri.parse('你的目标接口地址'), ); // 注入自定义请求头 request.headers.addAll(headers); // 传入序列化后的JSON业务数据,字段名和后端约定保持一致 request.fields['payload'] = businessPayloadJson; // 传入图片字段,支持两种常见来源: // 1. 本地文件路径来源 request.files.add( await http.MultipartFile.fromPath( 'image', // 字段名和后端接口的图片参数名保持一致 '本地图片的绝对文件路径', ) ); // 2. 内存字节流来源(比如从图片选择器拿到的Uint8List数据) // request.files.add( // http.MultipartFile.fromBytes( // 'image', // capturedImageBytes, // filename: 'upload_target.jpg', // contentType: MediaType('image', 'jpeg'), // ) // ); // 发送请求并获取响应 final response = await request.send(); final responseContent = await response.stream.bytesToString();
方案2:使用dio网络库实现
import 'package:dio/dio.dart'; final dioClient = Dio(); // 构建multipart表单数据 final formData = FormData.fromMap({ // 序列化的业务JSON数据 'payload': businessPayloadJson, // 本地文件图片字段 'image': await MultipartFile.fromFile( '本地图片的绝对文件路径', filename: 'upload_target.jpg', ), // 内存字节流图片替换写法 // 'image': MultipartFile.fromBytes( // capturedImageBytes, // filename: 'upload_target.jpg', // contentType: MediaType('image', 'jpeg'), // ), }); final response = await dioClient.post( '你的目标接口地址', data: formData, options: Options( headers: headers, // 不要手动设置Content-Type,dio会自动生成带boundary的multipart头 ), );
常见踩坑说明
- 字段名对齐:所有表单字段(不管是序列化JSON的字段名、还是图片的字段名)都要和后端接口的参数定义完全一致,否则后端无法解析拿到对应值
- 禁止未序列化直接传嵌套对象:如果直接把嵌套Dart对象放到multipart的fields里,客户端只会把对象转成无意义的
{...}字符串,服务端无法解析成合法JSON,必须主动调用jsonEncode做序列化 - 不要强制覆盖Content-Type:手动写死的multipart Content-Type会缺失boundary标识,是最高发的请求解析失败原因
内容的提问来源于stack exchange,提问作者Anoop
相关产品推荐
相关产品推荐

