1

我试图找出从 JavaScript 发起的 HTTP 请求上传/下载二进制数据到 Google AppEngine 的 Blobstore 的最低数据开销方式。理想情况下,我想直接提交二进制数据,即未编码的 8 位值;也许在一个看起来像这样的 POST 请求中:

...
Content-Type: multipart/form-data; boundary=boundary;

--boundary
Content-Disposition: form-data; name="a"; filename="b"
Content-Type: application/octet-stream

@#^%(^Qtr...
--boundary--

这里,@#^%(^Qtr...理想地表示任意 8 位二进制数据。

具体来说,我试图了解以下内容:

  • 是否可以直接上传 8 位二进制数据,或者我需要以某种方式对数据进行编码,例如 base-64 MIME 编码?
  • 如果我使用不同的编码,Blobstore 会将数据保存为内部的 8 位二进制还是编码格式?也就是说,base-64 编码会增加 33% 的存储成本吗?
  • 同理:编码开销会增加传出带宽成本吗?
  • 有没有更好的方法来格式化 POST 请求,所以我不需要想出一个boundary没有出现在我的二进制数据中的内容?例如,有没有办法指定 Content-Length 而不是边界?
  • 在检索数据的 GET 请求中,我是否可以简单地期望二进制数据以返回字符串结尾,或者服务器是否会以某种方式自动编码数据?
  • 如果我需要使用某种编码,在支持的基本上随机 8 位数据的选项中,哪一种是最佳选择?(base-64,UTF-8,其他?)
4

1 回答 1

1

尽管我因这个问题收到了Tumbleweed 徽章,但还是让我报告一下我的进度,以防有人在乎:

这个问题原来提出了 3 个独立的问题:

  1. 高效地将数据上传到 BlobStore
  2. 确保 BlobStore 以尽可能小的格式保存它
  3. 寻找可靠下载数据的方法

让我们从 (3) 开始,因为这最终会带来最大的问题:

到目前为止,我还没有找到通过 XHR 将真正的 8 位数据下载到浏览器的方法。使用诸如 application/octet-stream 之类的 mime 类型会导致只有 7 位可靠地到达客户端,除非将数据下载到文件中。我找到的最佳解决方案是对数据使用以下 mime 类型:

text/plain; charset=ISO-8859-1

我测试过的所有浏览器似乎都支持这一点:IE 8、Chrome 21、FF 12.0、Opera 11.61、Windows 下的 Safari 5.1.2 和 Android 2.3.3。

有了这个,几乎可以传输任何 8 位值,但有以下限制/警告:

  • 字符 0x00 在 IE8 中被解释为输入字符串的结尾,因此必须避免。
  • 大多数浏览器将字符集 ISO-8859-1 解释为 Windows-1252,从而导致字符 0x80 到 0x9F 相应更改。但是,这可以修复,因为更改是明确的。(见http://en.wikipedia.org/wiki/Windows-1252#Codepage_layout
  • 字符 0x81、0x8D、0x8F、0x90、0x9D 在 Windows-1252 字符集中保留,Opera 会为这些字符返回错误代码,因此也需要避免这些字符。

总的来说,我们可以使用 256 个字符中的 250 个。对于数据所需的基础更改,这意味着传出数据开销低于 0.5%,我想我可以接受。

所以,现在到问题(1)和(2):

由于传入带宽是免费的,我决定降低解决问题 (1) 的优先级以支持问题 (2) 和 (3)。事实证明,使用以下 POST 请求可以解决问题:

...
Content-Type: multipart/form-data; boundary=-

---
Content-Disposition: form-data; name="a"; filename="b"
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: base64

abcd==
-----

这里,abcd==是由上述 250 个允许字符组成的 base64-MIME 编码数据(参见http://en.wikipedia.org/wiki/Base64#Examples,GAE使用 + 和 / 作为最后 2 个字符)。编码是必要的(如果我错了,请纠正我),因为使用字符串数据调用 XHR send() 函数将导致字符串的 UTF-8 编码,这会破坏服务器接收到的数据。不幸的是,并非所有浏览器都可以将 ArrayBuffers 和 Blobs 传递给 send() 函数,从而更优雅地规避这个问题。

现在好消息:AppEngine BlobStore 会自动正确地解码这些数据,并在没有开销的情况下存储它!因此,使用 base64 编码只会导致客户端的数据上传速度变慢,但不会导致额外的托管成本(除非可能需要几个 CPU 周期进行解码)。

另外:AppEngine 开发服务器将在管理控制台和检索到的 BlobInfo 记录中报告存储的 blob 的编码大小(即大 33%)。但是,生产服务器没有这个问题,并且报告了正确的 blob 大小。

结论

使用 Content-Transfer-Encodingbase64上传 Content-Type 的二进制数据text/plain; charset=ISO-8859-1,其中可能不包含字符 0x00、0x81、0x8D、0x8F、0x90 和 0x9D,这会导致许多经过测试的浏览器的可靠数据传输具有存储/传出带宽开销不到半个百分点。base64 编码数据的上传开销为 33%,好于 UTF-8(对于随机 8 位数据)的预期 50%,但仍远非理想。

我不知道的是:这是最佳解决方案,还是可以做得更好?有人愿意挑战吗?

于 2012-08-04T17:22:58.663 回答