【问题标题】:Why is Ruby Base64 encoding conversion losing data and how I can route around?为什么 Ruby Base64 编码转换会丢失数据以及如何路由?
【发布时间】:2015-06-15 02:29:30
【问题描述】:

我正在构建一个与 Grape API 对话的 Rails UI。这是该程序的第二个实例。第一个实例运行良好。然而,第二个实例的 Grape API 似乎在通过网络发送数据之前破坏了数据。

我需要图像从文件 > json > http > db.现在我通过像这样发送文件来做到这一点:文件>字符串>编码到url安全的base64>to_json>http>解码>使用ActiveRecord保存到sqlite3 db。我被引导相信图像数据被我根据以下内容转换为base64而损坏。但是,由于 Grape 都是 JSON,因此必须在发送之前对字符进行编码(因为至少就 Ruby 的 JSON 库而言,无效的 UTF-8 == 无效的 JSON)。

所以我要么必须知道:

  1. 如何允许 Grape API 发送非 JSON(原始文件字符串)或
  2. 如何解码字符串并避免错误消息

打开文件并将其内容转换为 url 安全的 Base64。

File.open("#{folder}/#{file_name}", "rb:UTF-8") do |image|
  file_as_string = image.read
end
 => "iVBORw0K ... # truncated for length

事情马上变得很奇怪。 IRB 符合预期 - 编码为 UTF-8。

file_as_string.encoding.name
 => "UTF-8"

但是。服务器记录ASCII-8BIT。我无法解释这一点。每个文件都带有 Ruby 神奇的 UTF-8 注释。 Linux $LANG 设置为en_US.UTF-8

好的,但是当 Base64 转换时,无论如何我都会失去情节。即使在 IRB 中,从 UTF-8 开始,它也很隐蔽。为什么选择 US-ASCII?无论如何,为什么会失去兼容性?。

Base64.urlsafe_encode64(file_as_string).encoding.name
 => "US-ASCII"
Base64.urlsafe_decode64(Base64.urlsafe_encode64(file_as_string)).encoding.name
 => "ASCII-8BIT"
Base64.urlsafe_decode64(Base64.urlsafe_encode64(file_as_string)).encode("UTF-8")
Encoding::UndefinedConversionError: "\x89" from ASCII-8BIT to UTF-8
    from (irb):27:in `encode'
    from (irb):27
    from /home/me/.rvm/rubies/ruby-2.2.1/bin/irb:11:in `<main>'

请注意,IRB 中的错误与我相同 a) 在 Grape 尝试 to_json 之前不对字符串进行 base64 编码,b) 当我尝试解码并将 .save 字符串调用到模型属性时导轨一侧。

文件本身是二进制的(如果这很重要?)

$ file -bi /path/to/file.png
image/png; charset=binary

我尝试过或不愿意尝试的解决方案:

发送原始图像。读取

这是一个 JSON API,因此 Grape 在通过网络发送数据之前会转换为 JSON——这意味着任何响应都必须是有效的 JSON,据我所知。如果我尝试发送原始字符串,自动调用的.to_json 会引发相同的错误。

对结果进行强制编码

输出不是可读的 png。

降级

原始实例是 Ruby 1.9.2 和 CentOS 6.3。新实例是 Ruby 2.2.1 和 CentOS 7。我通常致力于向前发展,所以我宁愿开发一些解决方案,即使不向后兼容,然后回滚 Ruby 和我的操作系统。

不使用 UTF-8

Rails 的config/application.rbconfig.encoding = "utf-8" 行,config/environment.rbEncoding.default_external = Encoding::UTF_8; Encoding.default_internal = Encoding::UTF_8 行我希望不必为了这个问题而放弃UTF-8 兼容性。


那么有没有办法直接在 Grape 中提供文件,绕过 to_json 调用?或者是否有不同的编码安全用于 JSON-ing 和通过 http 发送?

【问题讨论】:

    标签: ruby encoding utf-8 centos grape-api


    【解决方案1】:

    PNG 文件没有字符编码。您应该在不声明字符编码的情况下打开文件。即使经过 base64 编码,您也无需关心字符集。

    文件经过 base64 编码后,结果为 7 位 ASCII 字符串,因此 encoding.name 报告 "US-ASCII"。这是您应该传递给框架的字符串,

    在 base64 编码之前不要在字符串上调用.encode() - 这肯定会损坏字符串。

    澄清一下:

    1. file_as_string 既不是 UTF-8,也不是 ASCII。它没有字符编码,因为它是二进制文件。 file_as_string.encoding.name 与你无关。
    2. Base64.urlsafe_encode64(file_as_string).encoding.name = "US-ASCII" 是正确的,因为您通过将二进制文件编码为 base64 有效地将其转换为文本/字符串。这确实有字符编码 - 7 位 ASCII。这就是你应该传递给 Grape 的东西。
    3. Base64.urlsafe_decode64(Base64.urlsafe_encode64(file_as_string)).encoding.name 无关紧要,因为结果又是一个 binary 字符串。 它没有字符编码。尝试.encode() 这会损坏数据。
    4. 您的 IRB 失败,因为您要求 Ruby 将二进制字符串转换为 UTF-8 文本编码。这就像拍照并要求将其转换为法语。

    【讨论】:

    • 你是对的,但不幸的是,我需要的最终结果是将字符串保存到 SQLite 3 数据库列。 (我试图澄清这一点。)我在 IRB 复制中调用.encode(),因为 Rails 在将字符串保存到数据库之前调用了.encode()。 IRB 只是演示字符串无法编码为 UTF-8 以及由此产生的错误。
    • @Sam 让我们注意不要将问题的范围更改为包含 Rails。在将注意力转向 Rails 之前,先让 Grape 部分工作。您的 IRB 不代表幕后发生的事情,因为 Grape 不应该在解码后的字符串上调用 .encode()。不要更改磁盘上的 .png 也不要设置编码。只需将 base64 字符串传递给 Grape。确认这与curl 一起使用,然后手动将结果进行base64 解码回图像。只有这样你才能开始使用 Rails。在 Rails 中获得 base64 字符串后,您应该可以保存到数据库
    • 抱歉 -- 没有尝试更改范围。 IRB 只是试图诊断这个问题。我的问题是并且仍然是关于编码的——我不明白 UTF-8 字符串如何通过简单地通过.encode64 然后通过.decode64 来包含无效的 UTF-8 字符。我希望得到答复。但是,我主要希望它能够为 API 和 Rails 应用程序提供良好的运行服务。
    • 嗨@Sam,我刚刚更新了我的答案以澄清一些事情。也许您可以粘贴从 Grape 获得的实际错误,这可能更能说明实际错误:)
    • 当 Grape .calls 我的方法和字符串不是 base64 编码时 -> Encoding::UndefinedConversionError: "\x89" from ASCII-8BIT to UTF-8。当 Rails .saves 模型实例属性的值 -> Encoding::UndefinedConversionError: "\x89" from ASCII-8BIT to UTF-8。但是,我会接受你的回答,因为它让我专注于正确的部分。忽略 Grape 端和 force_encoding("UTF-8") base64 和解码的字符串有效。我仍然不明白为什么...... \x89 来自哪里,为什么不用火杀死它会破坏图像?哦,好吧
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多