发送二进制数据响应的最有效方法(时间和空间)
我的项目是基于Flask的服务器。简单来说,这个服务器会根据一些算法计算(比如确定从AWS S3获取哪些文件名)来从S3获取二进制数据,然后把这些数据提供给一个使用HTML和JavaScript的客户端。
一开始,我觉得用JSON对象作为响应类型是最好的选择。我创建了一个JSON响应,格式可能有点错误:
{
'payload': [
{
'symbol': 'sym',
'exchange': 'exch',
'headerfile': {
'name': '#name',
'content': '#binarycontent'
},
'datafiles': [
{
'name': '#name',
'content': '#binarycontent'
},
{
'name': '#name',
'content': '#binarycontent'
}
]
},
'errors': [ //errors ]
}
如果JSON有语法错误,我在这里先道个歉;我有点困,没能找到小错误。构建这个JSON后,我发现JSON本身不支持二进制数据,所以我不能把二进制数据直接放进JSON里。
我意识到我可以把字节转换成base64编码的字符串,然后把这个字符串作为JSON的值。但是,转换后的字符串大小大约增加了30%;比如4010字节的数据被编码成了5348字节。虽然对于单个二进制数据块来说,这个增加不算什么,但对于我的客户来说,当需要在JSON响应中嵌入很多这样的二进制数据块时,这就成了一个问题。因为增加的大小会导致响应到达客户端的时间变长,这对客户的应用来说是个重要问题。
我考虑的另一个选项是将二进制数据块以octet-stream Content-Type
的形式流式传输给客户端。但我不确定这种方法是否比之前的方案更好。此外,我也没能弄清楚在这种情况下如何将二进制数据块和它们的名称关联起来。
有没有比“把二进制转换成文本然后嵌入JSON”更好的解决方案呢?
1 个回答
我解决了这个问题,写下这个解决方案,希望能帮到其他人节省时间。
感谢 @dstromberg 和 @LukasGraf 的建议。我先查看了 BSON,发现它满足我的需求,所以没有深入研究 Protocol Buffer。
在 PyPi 上,BSON 有两个包可用。在 pymongo 中,它是 MongoDB 的一个补充。在 bson 中,它是一个独立的包,显然更适合我的需求。不过,它只支持 Python2。
所以在自己动手移植之前,我先找了一下 Python3 的实现,发现了在 bsonspec.org 上有另一个 BSON 规范的实现:模块链接。
这个模块的最简单用法是这样的:
>>> import bson
warning: module typecheck.py cannot be imported, type checking is skipped
>>> encoded = bson.serialize_to_bytes({'name': 'chunkfile', 'content': b'\xad\x03\xae\x03\xac\x03\xac\x03\xd4\x13'})
>>> print(encoded)
b'1\x00\x00\x00\x02name\x00\n\x00\x00\x00chunkfile\x00\x05content\x00\n\x00\x00\x00\x00\xad\x03\xae\x03\xac\x03\xac\x03\xd4\x13\x00'
>>> decoded = bson.parse_bytes(encoded)
>>> print(decoded)
OrderedDict([('name', 'chunkfile'), ('content', b'\xad\x03\xae\x03\xac\x03\xac\x03\xd4\x13')])
如你所见,它也可以处理二进制数据。我从 Flask 发送数据时,设置了 mimetype=application/bson
,接收的 JavaScript 使用了 MongoDB 团队提供的这个独立 BSON 库,准确地解析了数据。