flutter - 如何在 dart (flutter) 中解析 http 多部分/混合响应

我想解析一个多部分/混合的 http 响应。关于我的问题,我找不到任何可用于 dart 的 sdk 或插件。我已经尝试过具有 MimeMultipartTransformermime 包,但它返回异常 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);
    });

任何反馈将不胜感激。

回答1

我已经解决了我的问题。

首先,您将导入此包:

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

我想我需要自己完全编写解析器。感谢开发此插件的 dart 开发人员为我节省了很多时间。

https://pub.dev/packages/mime

相似文章