curb
wygląda na świetne rozwiązanie, ale jeśli nie odpowiada Twoim potrzebom, możesz to zrobić Net::HTTP
. Post w postaci wieloczęściowej to po prostu starannie sformatowany ciąg znaków z kilkoma dodatkowymi nagłówkami. Wygląda na to, że każdy programista Ruby, który musi pisać wieloczęściowe posty, kończy pisanie własnej małej biblioteki, co sprawia, że zastanawiam się, dlaczego ta funkcja nie jest wbudowana. Może tak jest ... W każdym razie, dla przyjemności czytania, podam tutaj moje rozwiązanie. Ten kod jest oparty na przykładach, które znalazłem na kilku blogach, ale żałuję, że nie mogę już znaleźć linków. Więc wydaje mi się, że muszę przypisać sobie całą zasługę ...
Moduł, który napisałem w tym celu, zawiera jedną publiczną klasę do generowania danych formularza i nagłówków z haszu obiektów String
i File
. Na przykład, jeśli chcesz opublikować formularz z parametrem ciągu o nazwie „tytuł” i parametrem pliku o nazwie „dokument”, wykonaj następujące czynności:
#prepare the query
data, headers = Multipart::Post.prepare_query("title" => my_string, "document" => my_file)
Następnie po prostu robisz normalne POST
z Net::HTTP
:
http = Net::HTTP.new(upload_uri.host, upload_uri.port)
res = http.start {|con| con.post(upload_uri.path, data, headers) }
Lub jakkolwiek chcesz to zrobić POST
. Chodzi o to, że Multipart
zwraca dane i nagłówki, które musisz wysłać. I to wszystko! Proste, prawda? Oto kod modułu Multipart (potrzebujesz mime-types
klejnotu):
# Takes a hash of string and file parameters and returns a string of text
# formatted to be sent as a multipart form post.
#
# Author:: Cody Brimhall <mailto:brimhall@somuchwit.com>
# Created:: 22 Feb 2008
# License:: Distributed under the terms of the WTFPL (http://www.wtfpl.net/txt/copying/)
require 'rubygems'
require 'mime/types'
require 'cgi'
module Multipart
VERSION = "1.0.0"
# Formats a given hash as a multipart form post
# If a hash value responds to :string or :read messages, then it is
# interpreted as a file and processed accordingly; otherwise, it is assumed
# to be a string
class Post
# We have to pretend we're a web browser...
USERAGENT = "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/523.10.6 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6"
BOUNDARY = "0123456789ABLEWASIEREISAWELBA9876543210"
CONTENT_TYPE = "multipart/form-data; boundary=#{ BOUNDARY }"
HEADER = { "Content-Type" => CONTENT_TYPE, "User-Agent" => USERAGENT }
def self.prepare_query(params)
fp = []
params.each do |k, v|
# Are we trying to make a file parameter?
if v.respond_to?(:path) and v.respond_to?(:read) then
fp.push(FileParam.new(k, v.path, v.read))
# We must be trying to make a regular parameter
else
fp.push(StringParam.new(k, v))
end
end
# Assemble the request body using the special multipart format
query = fp.collect {|p| "--" + BOUNDARY + "\r\n" + p.to_multipart }.join("") + "--" + BOUNDARY + "--"
return query, HEADER
end
end
private
# Formats a basic string key/value pair for inclusion with a multipart post
class StringParam
attr_accessor :k, :v
def initialize(k, v)
@k = k
@v = v
end
def to_multipart
return "Content-Disposition: form-data; name=\"#{CGI::escape(k)}\"\r\n\r\n#{v}\r\n"
end
end
# Formats the contents of a file or string for inclusion with a multipart
# form post
class FileParam
attr_accessor :k, :filename, :content
def initialize(k, filename, content)
@k = k
@filename = filename
@content = content
end
def to_multipart
# If we can tell the possible mime-type from the filename, use the
# first in the list; otherwise, use "application/octet-stream"
mime_type = MIME::Types.type_for(filename)[0] || MIME::Types["application/octet-stream"][0]
return "Content-Disposition: form-data; name=\"#{CGI::escape(k)}\"; filename=\"#{ filename }\"\r\n" +
"Content-Type: #{ mime_type.simplified }\r\n\r\n#{ content }\r\n"
end
end
end