Ruby: How to post a file via HTTP as multipart/for

2019-01-01 14:23发布

I want to do an HTTP POST that looks like an HMTL form posted from a browser. Specifically, post some text fields and a file field.

Posting text fields is straightforward, there's an example right there in the net/http rdocs, but I can't figure out how to post a file along with it.

Net::HTTP doesn't look like the best idea. curb is looking good.

标签: ruby http post
12条回答
长期被迫恋爱
2楼-- · 2019-01-01 14:56

I like RestClient. It encapsulates net/http with cool features like multipart form data:

require 'rest_client'
RestClient.post('http://localhost:3000/foo', 
  :name_of_file_param => File.new('/path/to/file'))

It also supports streaming.

gem install rest-client will get you started.

查看更多
人间绝色
3楼-- · 2019-01-01 14:58

Here is my solution after trying other ones available on this post, I'm using it to upload photo on TwitPic:

  def upload(photo)
    `curl -F media=@#{photo.path} -F username=#{@username} -F password=#{@password} -F message='#{photo.title}' http://twitpic.com/api/uploadAndPost`
  end
查看更多
不流泪的眼
4楼-- · 2019-01-01 14:58

Fast forward to 2017, ruby stdlib net/http has this built-in since 1.9.3

Net::HTTPRequest#set_form): Added to support both application/x-www-form-urlencoded and multipart/form-data.

https://ruby-doc.org/stdlib-2.3.1/libdoc/net/http/rdoc/Net/HTTPHeader.html#method-i-set_form

We can even use IO which does not support :size to stream the form data.

Hoping that this answer can really help someone :)

P.S. I only tested this in ruby 2.3.1

查看更多
步步皆殇っ
5楼-- · 2019-01-01 14:58

restclient did not work for me until I overrode create_file_field in RestClient::Payload::Multipart.

It was creating a 'Content-Disposition: multipart/form-data' in each part where it should be ‘Content-Disposition: form-data’.

http://www.ietf.org/rfc/rfc2388.txt

My fork is here if you need it: git@github.com:kcrawford/rest-client.git

查看更多
高级女魔头
6楼-- · 2019-01-01 15:01

Ok, here's a simple example using curb.

require 'yaml'
require 'curb'

# prepare post data
post_data = fields_hash.map { |k, v| Curl::PostField.content(k, v.to_s) }
post_data << Curl::PostField.file('file', '/path/to/file'), 

# post
c = Curl::Easy.new('http://localhost:3000/foo')
c.multipart_form_post = true
c.http_post(post_data)

# print response
y [c.response_code, c.body_str]
查看更多
ら面具成の殇う
7楼-- · 2019-01-01 15:05

Another one using only standard libraries:

uri = URI('https://some.end.point/some/path')
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'If you need some headers'
form_data = [['photos', photo.tempfile]] # or File.read()/File.open() in case of local file

request.set_form form_data, 'multipart/form-data'
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| # pay attention to use_ssl if you need it
  http.request(request)
end

Tried a lot of approaches but only this was worked for me.

查看更多
登录 后发表回答