parseMultipart function
Parses multipart/form-data from raw bytes and boundary.
Implementation
FormData parseMultipart(List<int> body, String boundary) {
final fields = <String, String>{};
final files = <String, MultipartFile>{};
final boundaryBytes = utf8.encode('--$boundary');
final bodyBytes = Uint8List.fromList(body);
// Split by boundary
final parts = <Uint8List>[];
var start = 0;
while (start < bodyBytes.length) {
final idx = _indexOf(bodyBytes, boundaryBytes, start);
if (idx == -1) break;
if (start > 0) {
// Strip leading \r\n and trailing \r\n before boundary
var partEnd = idx - 2; // skip \r\n before boundary
if (partEnd > start) {
parts.add(bodyBytes.sublist(start, partEnd));
}
}
start = idx + boundaryBytes.length;
// Skip \r\n or -- after boundary
if (start < bodyBytes.length - 1 && bodyBytes[start] == 13 && bodyBytes[start + 1] == 10) {
start += 2;
} else if (start < bodyBytes.length - 1 && bodyBytes[start] == 45 && bodyBytes[start + 1] == 45) {
break; // final boundary --
}
}
for (final part in parts) {
// Find header/body separator (\r\n\r\n)
final sep = _indexOf(part, utf8.encode('\r\n\r\n'), 0);
if (sep == -1) continue;
final headerStr = utf8.decode(part.sublist(0, sep));
final content = part.sublist(sep + 4);
final disposition = RegExp(r'Content-Disposition:\s*form-data;\s*(.+)', caseSensitive: false)
.firstMatch(headerStr);
if (disposition == null) continue;
final params = disposition.group(1)!;
final nameMatch = RegExp(r'name="([^"]*)"').firstMatch(params);
if (nameMatch == null) continue;
final name = nameMatch.group(1)!;
final filenameMatch = RegExp(r'filename="([^"]*)"').firstMatch(params);
if (filenameMatch != null) {
final contentTypeMatch = RegExp(r'Content-Type:\s*(.+)', caseSensitive: false)
.firstMatch(headerStr);
files[name] = MultipartFile(
name: name,
filename: filenameMatch.group(1),
contentType: contentTypeMatch?.group(1)?.trim(),
bytes: Uint8List.fromList(content),
);
} else {
fields[name] = utf8.decode(content);
}
}
return FormData(fields, files);
}