You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter WebSocket传输大Base64图片时JSON解析失败问题

Flutter WebSocket大Base64图片解析失败问题

问题概述

在Flutter WebSocket开发场景中,客户端与服务器建立连接后发送Base64编码的图片列表,服务器端无法解析大尺寸图片:

  • 使用Isolate解析时,10-20KB的图片可正常解析
  • 图片超过100KB时,服务器端频繁出现解析错误

服务器端错误日志

flutter: Error in parsing : FormatException: Unexpected character (at character 1)
bgQpQ27iPvAA4GOfWrl69Q5fsNuJDi2WxtUAG8IAACZKivnBUT9KovtKJCVvSssK2phGwISmZJT...
^

flutter: Error in parsing : FormatException: Unexpected character (at character 1)
LjSgtxK1BSQoEbukA8iDxSN8/uStme7zUz/8gf1qCtUcYYQlS5CXECRwCoGO3+k079g0anRr65e...
^

flutter: Error in parsing : FormatException: Unexpected character (at character 1)
RjltlneHVEEpAKP7gcYM8HFCgoR4hbUUAEGDCZ5ic1YXV9ksMstttuBGU5KRzE0oypsOy44otpk...
^

flutter: Error in parsing : FormatException: Unexpected character (at character 1)
eGI0gVu4h5xaVAiEbs8YI95qivFtKdUEpTAngdSZp63cm4UNwy2QevUf4pO7IS9KUxBI9571bRS...
^

flutter: Error in parsing : FormatException: Unexpected character (at character 1)
KUMDn1qYtLBd5ejULhbbaQ3IbjccA8wadc+IL9VtbMWraLZktuKhIlQ27hE/LtVVf4ubtaoz4eB...
^

flutter: Error in parsing : FormatException: Unexpected character (at character 1)
+LYH3VAjvW1kV4ZFDLZBkGQe9O2zduhRK8kZEnpS0oTIIweKMgpJk8elJ2UqtF6ldssEJIjil3b...

解析用Isolate代码

class ServerLogsParser {
  final Uint8List buffer;

  String get encodedString => String.fromCharCodes(buffer);

  ServerLogsParser({required this.buffer});

  Future<ServerLogs?> parseInBackground() async {
    final p = ReceivePort();

    await Isolate.spawn(_decodeAndParseJson, p.sendPort);
    final res = await p.first;
    return res is ServerLogs ? res : null;
  }

  Future<void> _decodeAndParseJson(SendPort p) async {
    try {
      final jsonData = jsonDecode(encodedString);
      Isolate.exit(p, ServerLogs.fromMap(jsonData));
    } catch (e) {
      print("Error in parsing : $e");
      Isolate.exit(p, null);
    }
  }
}

服务器端连接处理代码

void handleConnection(Socket socket, Emitter<WebSocketState> emit) async {
    printWarning("Connection from Client: ${socket.remoteAddress}");
    socket.listen((event) async {
      final message = String.fromCharCodes(event);
      printSuccess("text from client: ${message}");
      final serverLogs =
          await ServerLogsParser(buffer: event).parseInBackground();
      if (serverLogs != null) {
        add(UpdateLogsEvent(serverLogs: serverLogs));
        print(serverLogs);
        if (serverLogs.action == ActionEnum.login) {
          add(
            HandleLogin(
              email: serverLogs.data['email'],
              password: serverLogs.data['password'],
              socket: socket,
            ),
          );
        }
      }
    }, onError: (error) {
      printError(error);
      clients.remove(socket);
      socket.close();
    }, onDone: () {
      printWarning("Server left");
      clients.remove(socket);
      socket.close();
    });
  }

问题根源与解决方案

核心原因

Socket的listen回调中,event是分片接收的二进制数据:当数据量较大(如100KB以上)时,WebSocket会将数据拆分成多个帧发送,服务器端单次event只能拿到部分数据。直接用这部分不完整的数据转字符串并解析JSON,必然出现格式错误。小尺寸图片数据量小,能一次性完整接收,所以解析正常。

解决方案

1. 拼接完整数据+标记边界

服务器端维护每个Socket的接收缓冲区,将分片数据拼接,直到检测到完整数据的边界标记(如换行符)后再解析。客户端发送数据时需添加该标记。

修改后的服务器端代码
void handleConnection(Socket socket, Emitter<WebSocketState> emit) async {
  printWarning("Connection from Client: ${socket.remoteAddress}");
  final buffer = BytesBuilder(); // 为每个连接维护独立缓冲区

  socket.listen((event) async {
    buffer.add(event);
    final fullData = buffer.takeBytes();
    final message = String.fromCharCodes(fullData);

    // 检测客户端约定的完整数据标记(此处以换行符为例)
    if (message.endsWith('\n')) {
      final trimmedMsg = message.trim();
      printSuccess("text from client: $trimmedMsg");
      try {
        final serverLogs = await ServerLogsParser(buffer: Uint8List.fromList(trimmedMsg.codeUnits)).parseInBackground();
        if (serverLogs != null) {
          add(UpdateLogsEvent(serverLogs: serverLogs));
          print(serverLogs);
          if (serverLogs.action == ActionEnum.login) {
            add(
              HandleLogin(
                email: serverLogs.data['email'],
                password: serverLogs.data['password'],
                socket: socket,
              ),
            );
          }
        }
      } catch (e) {
        printError("Parse error: $e");
      }
    } else {
      // 数据未完整,重新放回缓冲区
      buffer.add(fullData);
    }
  }, onError: (error) {
    printError(error);
    clients.remove(socket);
    socket.close();
  }, onDone: () {
    printWarning("Server left");
    clients.remove(socket);
    socket.close();
  });
}
客户端发送代码示例
// 客户端发送JSON时添加换行符作为完整标记
final jsonStr = jsonEncode({'images': base64ImageList});
webSocket.add('$jsonStr\n');

2. 额外优化建议

  • Isolate复用:当前每次解析都创建新Isolate,频繁销毁会损耗性能,可改为Isolate池复用机制。
  • 减少Base64开销:大图片的Base64编码会比原数据大30%,可直接发送二进制图片数据,降低传输体积和编码解码成本。

内容的提问来源于stack exchange,提问作者Agnel Selvan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 23:43:11