首页 > 解决方案 > 围绕 MongoDB/GridFS BinData 的困惑

问题描述

我正在使用 Python Mongoengine 将图像文件插入 GridFS,方法如下:

product = Product(name='New Product', price=20.0, ...)
with open(<IMAGE_FILE>, 'rb') as product_photo:
    product.image.put(product_photo_main, content_type='image/jpeg')
product.save()

当我使用 NoSQLBooster(或其他任何东西)查看此数据时,数据表示如下:

{
    "_id" : ObjectId("5d71263eae9a187374359927"),
    "files_id" : ObjectId("5d71263eae9a187374359926"),
    "n" : 0,
    "data" : BinData(0,"/9j/4AAQSkZJRgABAQEASABIAAD/4V6T...  more 261096 bytes - image/jpeg")
},

并且知道该"data"字段的 BinData 元组的第二部分包含 base64 编码,我很困惑在什么时候给出的原始字节open(<IMAGE_FILE>, 'rb')变成了 base64 编码?

此外,由于 base64 编码的大小比传输数据大 33% - 37%,这很糟糕,我该如何选择编码?至少阻止它使用base64 ...

我发现这个 SO question 提到了一种HexData数据类型

我还发现其他人也提到了子类型,这让我找到了关于 BSON 数据类型的内容。

Binary
Canonical Relaxed
{ "$binary":
   {
      "base64": "<payload>",
      "subtype": "<t>"
   }
}
<Same as Canonical>
Where the values are as follows:
"<payload>"
Base64 encoded (with padding as “=”) payload string.
"<t>"
A one- or two-character hex string that corresponds to a BSON binary subtype. See the extended bson documentation

http://bsonspec.org/spec.html可用的子类型。

这清楚地告诉我们有效载荷将是base64!

那么我可以改变这个,还是必须这样?

标签: python-3.xmongodbmongoenginebson

解决方案


在这一点上,原始字节......被base64编码

直接回答

仅在您选择在控制台上显示它们或通过其他一些“显示”格式显示它们时。以 BSON 格式跨线的本机格式不会有这个问题。

如果您选择不向终端或调试器显示内容,则它永远不会被编码为 base64 或任何其他格式。

修正点

这使我发现了有关 BSON 数据类型的信息。

这清楚地告诉我们有效载荷将是base64!

链接页面指的是 MongoDB 扩展 JSON,而不是有线 BSON 格式。

确实,扩展 JSON 将二进制编码为 base64,而 bson 本身并非如此。

如下所示,您的驱动程序将通过扩展 JSON 转换传递数据的唯一时间是在您要求它通过打印或调试向您显示内容的那一刻

细节

BSON 的 Spec(内部 mongodb 序列化格式)二进制文件是原生字节格式。

规范的相关部分:

binary  ::=     int32 subtype (byte*)

表示二进制对象是

  1. 字节*的长度,
  2. 后跟一个 1 字节的子类型
  3. 后跟原始字节

在字节“Hello\x00World”的情况下,中间包含一个空字节

“有线格式”将是

[11] [0x00] [Hello\x00World]

请注意,堆栈溢出,就像几乎每个驱动程序或显示终端都在与嵌入的空字节作斗争一样,几乎每个显示终端都会如此,除非系统明确表明空字节实际上包含在要显示的字节中。

意思是整数(打包成一个 32 位字节)后跟 1 字节子类型,然后是字面字节是实际跨线的内容。

正如您所指出的,大多数语言在将这个屏幕呈现给用户时都会遇到巨大的麻烦。

扩展 JSON是涉及将不可显示数据呈现到驱动程序中的最合适方式的规范。

对象 ID 不仅仅是字节,它们是可以表示时间戳的对象。

时间戳不仅仅是数字,它们可以表示时区并转换为根据用户时区显示。

二进制文件并不总是文本,其中可能包含有问题的字节,并且不破坏终端/gui/调试器的最简单方法是将它们简单地以某种 ASCII 格式(如 base64)编码。

记住

bson.Binary 和 GridFS 不应该以有线格式显示/打印/写入。传输层存在有线格式。

为了便于调试和打印语句,大多数驱动程序都实现了一种易于“显示”的格式,通过扩展 JSON 规范提取本机 BSON 格式。

如果您只是选择不显示/编码为扩展 JSON/调试/打印,则二进制字节实际上永远不会由驱动程序进行 base64 编码。


推荐阅读