curb
ressemble à une excellente solution, mais dans le cas où il ne répond pas à vos besoins, vous pouvez le faire avec Net::HTTP
. Un formulaire multipart post est soigneusement une chaîne au format des en-têtes. Il semble que chaque programmeur Ruby qui doit faire multipart postes finit d'écrire leur propre bibliothèque de peu pour elle, ce qui me fait me demander pourquoi cette fonctionnalité n'est pas intégré. C'est peut-être... en tout cas, pour votre plaisir de la lecture, je vais aller de l'avant et donner ma solution ici. Ce code est basé sur des exemples que j'ai trouvé sur un couple de blogs, mais je regrette que je ne peux pas trouver les liens de plus. Donc je suppose que j'ai juste à prendre tout le crédit pour moi-même...
Le module que j'ai écrit pour ce contient une classe publique, pour générer les données du formulaire et les en-têtes d'un algorithme de hachage String
et File
objets. Ainsi, par exemple, si vous voulais poster un formulaire avec un paramètre de chaîne nommée "titre" et un fichier de paramètre nommé "document", vous devez effectuer les opérations suivantes:
#prepare the query
data, headers = Multipart::Post.prepare_query("title" => my_string, "document" => my_file)
Alors que vous venez de faire une activité normale POST
avec Net::HTTP
:
http = Net::HTTP.new(upload_uri.host, upload_uri.port)
res = http.start {|con| con.post(upload_uri.path, data, headers) }
Ou de quelque autre façon que vous voulez faire l' POST
. Le point est que, Multipart
renvoie les données et les en-têtes que vous devez envoyer. Et c'est tout! Simple, non? Voici le code pour le Multipart module (vous avez besoin de l' mime-types
gem):
# 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