在单个字节字符串中编码多个 jpeg 图像的最简单可靠的方法是什么?

What's the simplest reliable way to encode multiple jpeg images in a single byte string?

我需要发布包含多个 jpeg 图像的 Google 云 Pub/Sub 消息。它需要进入数据body。将它作为 base64 编码的字符串放在属性中是行不通的,因为属性值被限制为 1024 字节: https://cloud.google.com/pubsub/quotas#resource_limits

执行此操作的简单可靠模式是什么?选择一些固定的分隔符似乎是可能的,但我想避免该分隔符出现在图像内部的可能性。 jpeg 字节数组中是否可能出现 |||| 之类的东西?另一种可能性似乎编码为 multi-part 哑剧,但我还没有找到任何 general-purpose non-http 库来做到这一点。我需要在 Java/Scala 和 Python 中实现。或者也许我可以在没有任何外部分隔符的情况下连接 jpeg 字节数组,并根据 header 标识符拆分它们?

看起来以下方法可能有效,用 Scala 编写,仅使用自然定界符:

  def serializeJpegs(jpegs: Seq[Array[Byte]]): Array[Byte] =
    jpegs.foldLeft(Array.empty[Byte])(_ ++ _)

  def deserializeJpegs(bytes: Array[Byte]): Seq[Array[Byte]] = {
    val JpegHeader = Array(0xFF.toByte, 0xD8.toByte)
    val JpegFooter = Array(0xFF.toByte, 0xD9.toByte)
    val Delimiter = JpegFooter ++ JpegHeader

    val jpegs: mutable.Buffer[Array[Byte]] = mutable.Buffer.empty
    var (start, end) = (0, 0)
    end = bytes.indexOfSlice(Delimiter, start) + JpegFooter.length

    while (end > JpegFooter.length) {
      jpegs += bytes.slice(start, end)
      start = end
      end = bytes.indexOfSlice(Delimiter, start) + JpegFooter.length
    }

    if (start < bytes.length) {
      jpegs += bytes.drop(start)
    }

    jpegs
  }

我确信还有更高效、更实用的实施方式,但那是以后的事了!

您可能希望使用 Avro or Protocol Buffers 之类的方式将数据存储在某种 schema-based 消息中。两者都可以生成可用于序列化和反序列化 Java/Scala 和 Python.

中消息的代码

例如,在协议缓冲区中,您可以在文件中创建消息 image.proto:

syntax = "proto3";

message Images {
  bytes images = 1;
}

您可以使用 protoc 编译器为此生成 python 代码:

 $ protoc -I=. --python_out=. image.proto 

在 Python3 中,要添加图像、序列化消息并发送它,您需要执行以下操作:

import image_pb2
from google.cloud import pubsub_v1

publisher = pubsub_v1.PublisherClient()
topic_path = publisher.topic_path(<project name>, <topic name>)

def send_images(images):
  img_msg = image_pb2.Images()
  for i in images:
    img_msg.images.append(i)

  msg_data = img_msg.SerializeToString()

  message_future = publisher.publish(topic_path, data=msg_data)
  print(message_future.result())

接收图像并处理它们:

import image_pb2
from google.cloud import pubsub_v1

def receive(message):
  images = image_pb2.Images()
  images.ParseFromString(message.data)
  for i in images.images:
    # Process the image
  message.ack()

subscriber = pubsub_v1.SubscriberClient()
subscription_path = subscriber.subscription_path(<project name>, <subscription name>)
subscribe_future = subscriber.subscribe(subscription_path, receive)
print(subscribe_future.result())