我想解析一个多部分/混合http响应。对于我的问题,我找不到任何可用的sdk或插件。我已经尝试过mime
包,它有一个MimeMultipartTransformer
,但是它返回一个异常Bad multipart ending
。
要解析示例内容的多部分/混合响应字符串(从utf8二进制文件转换而来):
HTTP/1.1 200 OK
Content-Type: multipart/mixed; boundary="7b1596fc4940bc1be725ad67f11ec1c4"
Date: Thu, 07 Nov 2013 15:10:16 GMT
Server: CouchDB (Erlang OTP)
Transfer-Encoding: chunked
--7b1596fc4940bc1be725ad67f11ec1c4
Content-Type: application/json
{
"_id": "SpaghettiWithMeatballs",
"_rev": "1-917fa23",
"_revisions": {
"ids": [
"917fa23"
],
"start": 1
},
"description": "An Italian-American delicious dish",
"ingredients": [
"spaghetti",
"tomato sauce",
"meatballs"
],
"name": "Spaghetti with meatballs"
}
--7b1596fc4940bc1be725ad67f11ec1c4
Content-Type: multipart/related; boundary="a81a77b0ca68389dda3243a43ca946f2"
--a81a77b0ca68389dda3243a43ca946f2
Content-Type: application/json
{
"_attachments": {
"recipe.txt": {
"content_type": "text/plain",
"digest": "md5-R5CrCb6fX10Y46AqtNn0oQ==",
"follows": true,
"length": 87,
"revpos": 7
}
},
"_id": "SpaghettiWithMeatballs",
"_rev": "7-474f12e",
"_revisions": {
"ids": [
"474f12e",
"5949cfc",
"00ecbbc",
"fc997b6",
"3552c87",
"404838b",
"5defd9d",
"dc1e4be"
],
"start": 7
},
"description": "An Italian-American delicious dish",
"ingredients": [
"spaghetti",
"tomato sauce",
"meatballs",
"love"
],
"name": "Spaghetti with meatballs"
}
--a81a77b0ca68389dda3243a43ca946f2
Content-Disposition: attachment; filename="recipe.txt"
Content-Type: text/plain
Content-Length: 87
1. Cook spaghetti
2. Cook meetballs
3. Mix them
4. Add tomato sauce
5. ...
6. PROFIT!
--a81a77b0ca68389dda3243a43ca946f2--
--7b1596fc4940bc1be725ad67f11ec1c4
Content-Type: application/json; error="true"
{"missing":"3-6bcedf1"}
--7b1596fc4940bc1be725ad67f11ec1c4--
这是我的测试代码:
String auth = _generateauth(couchUsername, couchPassword);
String url = couchServer +
"/" +
Uri.encodeComponent(couchDBName) +
"/" +
Uri.encodeComponent("patient/1001") +
"?open_revs=" +
Uri.encodeComponent("""["1-6bb2dd39beefd1dd2ae1f47c01caea37",
"2-logi",
"2-712b79fb62e64bbf3eab6c009334fb60",
"3-flower",
"4-bloom",
"3-b0c4b3b87373a410c4ad3710a39d7f81"
]""") +
"&latest=true";
http.Client client = http.Client();
http.Request rq = http.Request("GET", Uri.parse(url));
rq.headers.addAll({"Authorization": auth});
http.StreamedResponse sr = await client.send(rq);
if (sr.statusCode != 200) throw Exception("Server Error!");
if (sr.headers["server"] == null ||
!sr.headers["server"]!.contains("CouchDB"))
throw Exception("Invalid Server: Invalid Transfer Encoding");
print(sr.headers);
RegExp boundaryget = RegExp('boundary="(.+)"');
String contentType = sr.headers["content-type"].toString();
final match = boundaryget.firstMatch(contentType);
String boundary = match?.group(1) as String;
List<int> completeResponse = await sr.stream.toBytes();
List<List<int>> cr = [completeResponse];
Stream<MimeMultipart> mm =
MimeMultipartTransformer(boundary).bind(Stream.fromIterable(cr));
mm.listen((event) {
print(event);
});
如有任何反馈,将不胜感激。
发布于 2022-05-18 20:22:28
我已经解决了我的问题。
首先,您将导入以下软件包:
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import "package:http/http.dart" as http;
import 'package:mime/mime.dart';
这是密码:
Future<void> openRevs() async {
String auth = _generateauth(couchUsername, couchPassword);
String url = couchServer +
"/" +
Uri.encodeComponent(couchDBName) +
"/" +
Uri.encodeComponent("patient/1001") +
"?open_revs=" +
Uri.encodeComponent("""["1-6bb2dd39beefd1dd2ae1f47c01caea37",
"2-logi",
"2-712b79fb62e64bbf3eab6c009334fb60",
"3-flower",
"4-bloom",
"3-b0c4b3b87373a410c4ad3710a39d7f81"
]""") +
"&latest=true";
http.Client client = http.Client();
http.Request rq = http.Request("GET", Uri.parse(url));
rq.headers.addAll({"Authorization": auth});
http.StreamedResponse sr = await client.send(rq);
if (sr.statusCode != 200) throw Exception("Server Error!");
if (sr.headers["server"] == null ||
!sr.headers["server"]!.contains("CouchDB"))
throw Exception("Invalid Server: Not a CouchDB Server!");
print(sr.headers);
// Generate REGEX to get boundary from content-type header
RegExp boundaryget = RegExp('boundary="(.+)"');
String contentType = sr.headers["content-type"].toString();
// Get the boundary
final match = boundaryget.firstMatch(contentType);
String boundary = match?.group(1) as String;
// Get the completed response from the server
List<int> completeResponse = await sr.stream.toBytes();
// Create a list for stream creation..
List<List<int>> cr = [completeResponse];
// generate MimeMultipart Stream
Stream<MimeMultipart> mm =
MimeMultipartTransformer(boundary).bind(Stream.fromIterable(cr));
// We put the parsed responses here...
List<MimeMultipart> mixedResponse = [];
List<MimeMultipart> relatedResponse = [];
// Completer. So we can wait for the two response results.
Completer<bool> allDone = Completer<bool>();
Completer<bool> relatedComplete = Completer<bool>();
mm.listen((event) async {
String contentType = event.headers["content-type"].toString();
// If content-type is multipart/related, we need to reparse it with MimeMultiPartTransformer
if (contentType.contains("multipart/related")) {
List<List<int>> data = await event.toList();
Stream<List<int>> myStream = Stream.fromIterable(data);
final match = boundaryget.firstMatch(contentType);
boundary = match?.group(1) as String;
Stream<MimeMultipart> responseData =
MimeMultipartTransformer(boundary).bind(myStream);
responseData.listen((event) {
// Add to relatedResponse
relatedResponse.add(event);
}, onDone: () => relatedComplete.complete(true),
onError: (er) => relatedComplete.complete(false));
} else {
// Add to mixedResponse
mixedResponse.add(event);
}
}, onDone: () => allDone.complete(true), onError: (e) => allDone.complete(false));
// Lets wait for the results...
await relatedComplete.future;
await allDone.future;
// Here are the results.
print("Mixed Response Length: " + mixedResponse.length.toString());
print("Related Response Length: " + relatedResponse.length.toString());
for (MimeMultipart response in mixedResponse) {
print("Headers: " + response.headers.toString());
List<String> body = await response.single
.asStream()
.map(((event) => utf8.decode(event, allowMalformed: true)))
.toList();
print("Content Body: " + body[0].toString());
}
for (MimeMultipart response in relatedResponse) {
print(response.headers);
List<String> body = await response.single
.asStream()
.map(((event) => utf8.decode(event, allowMalformed: true)))
.toList();
print("Content Body: " + body[0].toString());
// That's all.
}
}
调试控制台将显示如下:
flutter: {x-couchdb-body-time: 0, x-couch-request-id: b2930f11cd, content-type: multipart/mixed; boundary="b9c676d8e38719f6db939e000e1584f4", transfer-encoding: chunked, date: Wed, 18 May 2022 20:08:11 GMT, server: CouchDB/3.2.0 (Erlang OTP/22)}
flutter: Mixed Response Length: 2
flutter: Related Response Length: 2
flutter: Headers: {content-type: application/json}
flutter: Content Body: {"_id":"patient/1001","_rev":"5-d1eea3934c7a179636ca3021e30615cf","_deleted":true}
flutter: Headers: {content-type: application/json; error="true"}
flutter: Content Body: {"missing":"2-logi"}
flutter: {content-type: application/json}
flutter: Content Body: {"_id":"patient/1001","_rev":"5-738ce01d9989bc16084b01572871d9e6","first_name":"Amees - Sun & Moon","_attachments":{"Screen Shot 2022-05-17 at 11.32.22 AM.png":{"content_type":"image/png","revpos":4,"digest":"md5-irm/EL7L1BiKmfimBIWLIw==","length":156857,"follows":true}}}
flutter: {content-disposition: attachment; filename="Screen Shot 2022-05-17 at 11.32.22 AM.png", content-type: image/png, content-length: 156857}
flutter: Content Body: �PNG
\^Z
我想我需要自己完整地编写解析器。感谢省道开发人员,他们开发了这个插件,为我节省了很多时间。
https://stackoverflow.com/questions/72294203
复制相似问题