flutter_curl
flutter_curl copied to clipboard
MimeMultipartException: Bad multipart ending
Excpected goal: I want to upload files to multipart API using these plugins:
- shelf_multipart on the API side
- this flutter_curl plugin on the mobile side
but it gives me "MimeMultipartException: Bad multipart ending", however it works perfectly executed in CMD as shown below
curl -H "Content-Type: multipart/mixed" -F id=2 -F [email protected] -F [email protected] -F [email protected] -F [email protected] -F [email protected] -F [email protected] -F [email protected] -F [email protected] http://127.0.0.1:8080/test_upload
HOW I can achive this using flutter_curl? or what is the solution to this problem?
here are some supporting attachments:
- server side
Future<Response> upload(Request request) async {
try {
// final description = StringBuffer('Regular multipart request\n');
int audioFileCount = 1;
Map<String, String> data = {};
await for (final part in request.parts) {
String content = part.headers['content-disposition'];
if (content.contains('name="id"')) {
data.addAll({
'id': await part.readString(),
});
} else if (content.contains('name="audio_$audioFileCount"')) {
Uint8List file = await part.readBytes();
data.addAll({
'mfcc_$audioFileCount': extractMfcc(file)
.toString(), // looks like --> mfcc_1: [[10.1209..], [10.1217..], ..]
});
audioFileCount++;
}
}
print(data);
if (audioFileCount != 9) {
return Response.forbidden('Fields not filled perfectly.');
}
Map<String, dynamic> result = await model.updateMFCC(data);
if (result['status']) {
return Response.ok(json.encode({
'status': true,
'message': 'Complete processing.',
}));
} else {
return Response.ok(json.encode({
'status': false,
'message': 'Processing failed.',
}));
}
} on FormatException catch (e) {
return Response.internalServerError(
body: '!!EXCEPTION!!\n${e.toString()}\n${e.message.toString()}');
} on Exception catch (e) {
print(e
.toString()); // Produced: "MimeMultipartException: Bad multipart ending"
return Response.internalServerError(
body: '!!EXCEPTION!!\n${e.toString()}');
}
}
- mobile side
String path = 'test_upload';
APIMethod method = APIMethod.post;
Map<String, String>? parameters = {'id': '1'};
List<File> files = const [File('path/to/file.wav'), File('path/to/file.wav'), ...];
List<String> fields = const ['audio_1', 'audio_2', ...];
Client client = Client(
verbose: true,
interceptors: [
// HTTPCaching(),
],
);
await client.init();
List<Multipart> multiparts = [];
parameters.forEach((key, value) {
multiparts.add(Multipart(
name: key,
data: value,
));
});
if (files.isNotEmpty && fields.isNotEmpty) {
if (files.length == fields.length) {
for (var i = 0; i < files.length; i++) {
multiparts.add(
Multipart.file(
name: fields[i],
path: files[i].path,
filename: "audio_$i.wav",
),
);
}
} else {
if (withPop) context.loaderOverlay.hide();
showFlushbar(context, 'file and field count doesnt same.',
color: Colors.red);
return {
'status': false,
'message': 'file and field count doesnt same.',
};
}
}
final res = await client.send(Request(
method: method == APIMethod.post ? "POST" : "GET",
url: "http://$baseUrl/$path",
headers: {"Content-Type": "multipart/mixed"},
// body: RequestBody.raw(utf8.encode("hello world")),
// body: RequestBody.string("hello world"),
// body: RequestBody.form({"age": "10", "hello": "world"}),
body: RequestBody.multipart(multiparts),
));
List<int> resBody = res.body;
Map<String, dynamic> resMap = res.headers;
String resText = res.text();
print('body: $resBody');
print('map: $resMap');
print('text: $resText');
- output of number 2 above
I/flutter (31072): libcurl/7.72.0-DEV BoringSSL zlib/1.2.11 brotli/1.0.1 nghttp2/1.42.0
I/flutter (31072): body: [33, 33, 69, 88, 67, 69, 80, 84, 73, 79, 78, 33, 33, 10, 77, 105, 109, 101, 77, 117, 108, 116, 105, 112, 97, 114, 116, 69, 120, 99, 101, 112, 116, 105, 111, 110, 58, 32, 66, 97, 100, 32, 109, 117, 108, 116, 105, 112, 97, 114, 116, 32, 101, 110, 100, 105, 110, 103]
I/flutter (31072): map: {date: Wed, 29 Mar 2023 14:52:09 GMT, content-length: 58, x-frame-options: SAMEORIGIN, content-type: text/plain; charset=utf-8, x-xss-protection: 1; mode=block, x-content-type-options: nosniff, server: dart:io with Shelf}
I/flutter (31072): text: !!EXCEPTION!!
I/flutter (31072): MimeMultipartException: Bad multipart ending
- flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 3.7.8, on Microsoft Windows [Version 10.0.19044.1526], locale ja-JP)
[√] Windows Version (Installed version of Windows is version 10 or higher)
Checking Android licenses is taking an unexpectedly long time...[√] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
[√] Chrome - develop for the web
[√] Visual Studio - develop for Windows (Visual Studio Community 2022 17.2.6)
[√] Android Studio (version 2020.3)
[!] Android Studio (version 4.1)
X Unable to determine bundled Java version.
[√] VS Code (version 1.76.2)
[√] Connected device (4 available)
[√] HTTP Host Availability
! Doctor found issues in 1 category.
Could you provide a minimal server example?
From the first look at the code headers: {"Content-Type": "multipart/mixed"},
could be an issue.
For multipart requests curl will automatically generate a header for this. And multipart requests require the header to be of the format multipart/form-data; boundary=abcdxyz
where abcdxyz
is the key that will separate the different fields of the request. And this is generated dynamically to avoid the collision with field contents.
Do you have a specific reason to use multipart/mixed
? Does shelf require this?