如何在 Ruby Sinatra 中读取 GZIP 负载
How to read a GZIP payload in Ruby Sinatra
在远程主机上,我有一个 bash 脚本将一个简单的 gzip 压缩 YAML 文件发送到我的 Ruby Sinatra 端点:
#!/bin/bash
/bin/gzip -c /tmp/test.yaml > /tmp/test.gz
curl -i <hostname>:<port>/last_run_report -H "Content-Type: application/xml" -H "Content-Encoding: gzip" --data-binary @/tmp/test.gz
我的示例 Ruby 应用程序是:
require 'sinatra'
require 'zlib'
require 'stringio'
set :port, <port>
set :bind, "<ip>"
post '/last_run_report' do
sio = StringIO.new(request.body.to_s)
gz = Zlib::GzipReader.new(sio).read
test_yaml = YAML.load(gz)
end
这给了我以下错误:
Zlib::GzipFile::Error: not in gzip format
如果我需要 'base64' 并将端点定义更改为:
post '/last_run_report' do
raw = Base64.decode64(request.body)
sio = StringIO.new(raw)
gz = Zlib::GzipReader.new(sio).read
test_yaml = YAML.load(gz)
end
我收到以下错误:
NoMethodError: undefined method `unpack1' for #<StringIO:0x000055713e2d51b8>
我不知道是我发送的数据有误还是读取有误。请帮忙。
假设 YAML 示例如下所示:
martin:
name: Martin D'vloper
job: Developer
skill: Elite
您不需要所有多余的 StringIO
东西。 request.body
已经是一个 StringIO
实例,因此无需将其转换为字符串然后再将其转换为 StringIO
:
require 'sinatra'
require 'zlib'
post '/last_run_report' do
gz = Zlib::GzipReader.new(request.body).read
puts YAML.load(gz)
end
现在提出您的要求:
curl -i localhost:4567/last_run_report -H "Content-Type: application/xml" -H "Content-Encoding: gzip" --data-binary @test.gz
并查看 sinatra 输出:
== Sinatra (v2.0.4) has taken the stage on 4567 for development with backup from Thin
Thin web server (v1.7.2 codename Bachmanity)
Maximum connections set to 1024
Listening on localhost:4567, CTRL+C to stop
{"martin"=>{"name"=>"Martin D'vloper", "job"=>"Developer", "skill"=>"Elite"}}
::1 - - [14/Jan/2019:23:24:28 -0700] "POST /last_run_report HTTP/1.1" 200 - 0.0048
请注意 puts
已将 {"martin"=>{"name"=>"Martin D'vloper", "job"=>"Developer", "skill"=>"Elite"}}
写入控制台。
我应该指出,在您的示例中,以下代码无法按您预期的方式工作:
sio = StringIO.new(request.body.to_s)
您希望能够调用 sio.read
并得到如下信息:
\x1F\x8B\b\b\xA7z=\\x00\x03test.yaml\x00SVp\xCCSH\xCD-\xC8\xC9\xAFLMU(JM\xCE/J\xE1\xCAM,*\xC9\xCC\xB3\xE2R\x00\x82\xBC\xC4\xDCT+\x05_\xB0\x88\x82\x8BzYN~Aj\x11X&+?\xC9J\xC1%\xB5,\x15!T\x9C\x9D\x99\x93c\xA5\xE0\x9A\x93Y\x92\n\x00\xFC\xA0\x83yZ\x00\x00\x00
你实际得到的是:
#<StringIO:0x00007ffd8184bdf0>
请注意,这是文字字符串“#<StringIO:0x00007ffd8184bdf0>
”而不是对 StringIO
对象的引用,因为这是调用 .to_s
时 returned 的内容在像 request.body
这样的 StringIO
对象上,因此对 sio.read
的任何后续调用(隐含在对 Zlib::GzipReader.new
的调用中)将 return 该文字字符串并将不是 return 您期望的压缩数据 return,这会导致错误 Zlib::GzipFile::Error: not in gzip format
.
当您想要 return StringIO
的字符串表示时,您应该调用 .string
or .read
,而不是 .to_s
。考虑到这一点,使您的第一个示例工作所需的最小更改是更改此:
sio = StringIO.new(request.body.to_s)
为此:
sio = StringIO.new(request.body.string)
但同样,这是从 StringIO
到字符串再返回到 StringIO
的不必要的转换。
在远程主机上,我有一个 bash 脚本将一个简单的 gzip 压缩 YAML 文件发送到我的 Ruby Sinatra 端点:
#!/bin/bash
/bin/gzip -c /tmp/test.yaml > /tmp/test.gz
curl -i <hostname>:<port>/last_run_report -H "Content-Type: application/xml" -H "Content-Encoding: gzip" --data-binary @/tmp/test.gz
我的示例 Ruby 应用程序是:
require 'sinatra'
require 'zlib'
require 'stringio'
set :port, <port>
set :bind, "<ip>"
post '/last_run_report' do
sio = StringIO.new(request.body.to_s)
gz = Zlib::GzipReader.new(sio).read
test_yaml = YAML.load(gz)
end
这给了我以下错误:
Zlib::GzipFile::Error: not in gzip format
如果我需要 'base64' 并将端点定义更改为:
post '/last_run_report' do
raw = Base64.decode64(request.body)
sio = StringIO.new(raw)
gz = Zlib::GzipReader.new(sio).read
test_yaml = YAML.load(gz)
end
我收到以下错误:
NoMethodError: undefined method `unpack1' for #<StringIO:0x000055713e2d51b8>
我不知道是我发送的数据有误还是读取有误。请帮忙。
假设 YAML 示例如下所示:
martin:
name: Martin D'vloper
job: Developer
skill: Elite
您不需要所有多余的 StringIO
东西。 request.body
已经是一个 StringIO
实例,因此无需将其转换为字符串然后再将其转换为 StringIO
:
require 'sinatra'
require 'zlib'
post '/last_run_report' do
gz = Zlib::GzipReader.new(request.body).read
puts YAML.load(gz)
end
现在提出您的要求:
curl -i localhost:4567/last_run_report -H "Content-Type: application/xml" -H "Content-Encoding: gzip" --data-binary @test.gz
并查看 sinatra 输出:
== Sinatra (v2.0.4) has taken the stage on 4567 for development with backup from Thin
Thin web server (v1.7.2 codename Bachmanity)
Maximum connections set to 1024
Listening on localhost:4567, CTRL+C to stop
{"martin"=>{"name"=>"Martin D'vloper", "job"=>"Developer", "skill"=>"Elite"}}
::1 - - [14/Jan/2019:23:24:28 -0700] "POST /last_run_report HTTP/1.1" 200 - 0.0048
请注意 puts
已将 {"martin"=>{"name"=>"Martin D'vloper", "job"=>"Developer", "skill"=>"Elite"}}
写入控制台。
我应该指出,在您的示例中,以下代码无法按您预期的方式工作:
sio = StringIO.new(request.body.to_s)
您希望能够调用 sio.read
并得到如下信息:
\x1F\x8B\b\b\xA7z=\\x00\x03test.yaml\x00SVp\xCCSH\xCD-\xC8\xC9\xAFLMU(JM\xCE/J\xE1\xCAM,*\xC9\xCC\xB3\xE2R\x00\x82\xBC\xC4\xDCT+\x05_\xB0\x88\x82\x8BzYN~Aj\x11X&+?\xC9J\xC1%\xB5,\x15!T\x9C\x9D\x99\x93c\xA5\xE0\x9A\x93Y\x92\n\x00\xFC\xA0\x83yZ\x00\x00\x00
你实际得到的是:
#<StringIO:0x00007ffd8184bdf0>
请注意,这是文字字符串“#<StringIO:0x00007ffd8184bdf0>
”而不是对 StringIO
对象的引用,因为这是调用 .to_s
时 returned 的内容在像 request.body
这样的 StringIO
对象上,因此对 sio.read
的任何后续调用(隐含在对 Zlib::GzipReader.new
的调用中)将 return 该文字字符串并将不是 return 您期望的压缩数据 return,这会导致错误 Zlib::GzipFile::Error: not in gzip format
.
当您想要 return StringIO
的字符串表示时,您应该调用 .string
or .read
,而不是 .to_s
。考虑到这一点,使您的第一个示例工作所需的最小更改是更改此:
sio = StringIO.new(request.body.to_s)
为此:
sio = StringIO.new(request.body.string)
但同样,这是从 StringIO
到字符串再返回到 StringIO
的不必要的转换。