如何使用Ruby gem通过HTTP请求发送二进制数据?

10

我正在尝试找到一种方法来复制发送二进制数据负载并设置Content-Type: binary头的HTTP请求,就像使用cURL执行以下命令一样:

echo -e '\x14\x00\x00\x00\x70\x69\x6e\x67\x00\x00' | curl -X POST \
-H 'Content-Type: binary' \
-H 'Accept: */*' \
-H 'Accept-Encoding: gzip,deflate,sdch' \
-H 'Accept-Language: en-US,en;q=0.8,pt;q=0.6' \
-H 'Cookie: JSESSIONID=m1q1hkaptxcqjuvruo5qugpf' \
--data-binary @- \
--url 'http://202.12.53.123' \
--trace-ascii /dev/stdout

我已经尝试使用REST客户端 (https://github.com/rest-client/rest-client) 和HTTPClient (https://github.com/nahi/httpclient),但是没有成功。使用下面的代码,服务器响应了HTTP 500。有人之前做过这件事吗?还是宝石设计的目的不可能实现?
require 'rest-client'
request = RestClient::Request.new(
  :method => :post, 
  :url => 'http://202.12.53.123', 
  :payload => %w[14 00 00 00 70 69 6e 67 00 00], 
  :headers => {
    :content_type => :binary,
    :accept => '*/*',
    :accept_encoding => 'gzip,deflate,sdch',
    :accept_language => 'en-US,en;q=0.8,pt;q=0.6',
    :cookies => {'JSESSIONID' => 'm1q1hkaptxcqjuvruo5qugpf'}
  }
)
request.execute

更新(带一个可能的解决方案)

最终我使用了HTTParty来发送请求(根据@DemonKingPiccolo的指示),并且它起作用了。以下是代码:

require 'httparty'
hex_data = "14 00 00 00 70 69 6e 67 00 00"
response = HTTParty.post(
  'http://202.12.53.123', 
  :headers => {
    'Content-Type' => 'binary',
    'Accept-Encoding' => 'gzip,deflate,sdch',
    'Accept-Language' => 'en-US,en;q=0.8,pt;q=0.6'
  },
  :cookies => {'JSESSIONID' => 'm1q1hkaptxcqjuvruo5qugpf'},
  :body => [hex_data.gsub(/\s+/,'')].pack('H*').force_encoding('ascii-8bit')
)
puts response.body, response.code, response.message, response.headers.inspect
标签也可以按照@gumbo的建议编写:
%w[14 00 00 00 70 69 6e 67 00 00].map { |h| h.to_i(16) }.map(&:chr).join

1
可能 httparty 能够做到这一点。 - Piccolo
2
%w[14 00 00 00 70 69 6e 67 00 00]并不是你想象中的那样。它创建了一个等同于["14", "00", "00", "00", "70", "69", "6e", "67", "00", "00"]的数组。也许你更想要的是%w[14 00 00 00 70 69 6e 67 00 00].map { |h| h.to_i(16) }.map(&:chr).join - Gumbo
2
顺便提一下,在“Content-Type”头部中,“binary”不是有效的值。“Content-Type”期望一个以MIME类型格式表示的值,即“type/subtype”。当你POST二进制数据时,通常需要使用“application/octet-stream”。 - Jordan Running
1
为什么不像我的示例那样使用带有转义字符的字符串,而是在hex_data上执行所有这些操作(gsubpackmap等)? "\x14\x00\x00\x00\x70\x69\x6e\x67\x00\x00"将产生完全相同的结果。 - Jordan Running
1
或者,如果你有一个非常强烈的理由需要将字节作为十六进制字符串,那么 %w[14 00 00 ...].map {|h| h.to_i(16) }.pack('c*')map.map.join 版本可以节省几个操作。 - Jordan Running
显示剩余3条评论
2个回答

8

我刚试过这个,它完美地运行了:

require "net/http"

uri = URI("http://example.com/")

http = Net::HTTP.new(uri.host, uri.port)

req = Net::HTTP::Post.new(uri.path)
req.body = "\x14\x00\x00\x00\x70\x69\x6e\x67\x00\x00"
req.content_type = "application/octet-stream"

http.request(req)
# => #<Net::HTTPOK 200 OK readbody=true>

我使用RequestBin验证了数据正确地POST。 Net::HTTP非常粗糙,不太好用(例如,您必须手动格式化Cookie头)。它的主要优点是它在标准库中。像RestClient或HTTParty这样的gem可能是更好的选择,我相信它们中的任何一个都可以轻松处理二进制数据。

感谢@jordan向我介绍了Net::HTTP。我已经编辑了问题描述,并提供了我找到的其他可能的解决方案。 - Igor de Lorenzi
以下代码行:req = NET::HTTP::Post.new(uri.path) 中有一个拼写错误,正确的应该是:req = Net::HTTP::Post.new(uri.path)。 - raskhadafi

0
略有不同的推理,但希望能有所帮助。
我的HTTP请求只接受application/json类型,因此二进制数据需要显示为PDF文件,并通过请求体发送。
从二进制数据创建了一个临时文件,并使用HTTParty之类的工具将其通过请求发送,类似于:
pdf_file = binary_data # can use gem like WickedPdf.new.pdf_from_string(content)

Tempfile.create(["file-name", ".pdf"], binmode: true) do |temp_file|
   temp_file.write(pdf_file)
   body = { "document" : File.open(temp_file) }
   HTTParty.post(url, headers: { "Content-Type": "application/json" }, body: body)
end

关于 Ruby 的临时文件


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接