curb
виглядає як чудове рішення, але якщо воно не відповідає вашим потребам, ви можете це зробити Net::HTTP
. Повідомлення з багаточастинкової форми - це лише ретельно відформатований рядок із додатковими заголовками. Здається, що кожен програміст Ruby, якому потрібно робити багатоповерхові пости, закінчує писати для цього свою маленьку бібліотеку, що змушує мене замислитися, чому ця функціональність не вбудована. Можливо, так і є ... У всякому разі, для вашого задоволення від читання я піду далі і дам тут своє рішення. Цей код заснований на прикладах, які я знайшов у кількох блогах, але шкодую, що більше не можу знайти посилання. Тож я гадаю, що я просто повинен взяти на себе всю заслугу ...
Я написав для цього модуль містить один загальнодоступний клас для генерування даних форми та заголовків із хешу String
та File
об’єктів. Так, наприклад, якщо ви хочете опублікувати форму з параметром рядка під назвою "title" та параметром файлу з назвою "документ", ви зробите наступне:
#prepare the query
data, headers = Multipart::Post.prepare_query("title" => my_string, "document" => my_file)
Тоді ви просто зробите звичайне POST
з Net::HTTP
:
http = Net::HTTP.new(upload_uri.host, upload_uri.port)
res = http.start {|con| con.post(upload_uri.path, data, headers) }
Або все-таки ви хочете зробити це POST
. Справа в тому, що Multipart
повертає дані та заголовки, які вам потрібно надіслати. І це все! Просте, правда? Ось код для модуля Multipart (вам потрібен mime-types
дорогоцінний камінь):
# 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