curb
sembra un'ottima soluzione, ma nel caso in cui non soddisfi le tue esigenze, puoi farlo con Net::HTTP
. Un post in un modulo multiparte è solo una stringa formattata con cura con alcune intestazioni extra. Sembra che ogni programmatore Ruby che ha bisogno di fare post in più parti finisca per scrivere la propria piccola libreria per questo, il che mi fa chiedere perché questa funzionalità non è integrata. Forse è ... Comunque, per il tuo piacere di lettura, vado avanti e fornisco la mia soluzione qui. Questo codice si basa su esempi che ho trovato su un paio di blog, ma mi dispiace di non riuscire più a trovare i collegamenti. Quindi immagino di dover prendere tutto il merito per me stesso ...
Il modulo che ho scritto per questo contiene una classe pubblica, per generare i dati del modulo e le intestazioni da un hash di String
e File
oggetti. Quindi, ad esempio, se volessi pubblicare un modulo con un parametro stringa denominato "titolo" e un parametro file denominato "documento", dovresti fare quanto segue:
#prepare the query
data, headers = Multipart::Post.prepare_query("title" => my_string, "document" => my_file)
Quindi fai solo una normale POST
con Net::HTTP
:
http = Net::HTTP.new(upload_uri.host, upload_uri.port)
res = http.start {|con| con.post(upload_uri.path, data, headers) }
O comunque tu voglia fare il file POST
. Il punto è che Multipart
restituisce i dati e le intestazioni che devi inviare. E questo è tutto! Semplice, vero? Ecco il codice per il modulo Multipart (serve la mime-types
gemma):
# 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