curb
semble être une excellente solution, mais au cas où elle ne répondrait pas à vos besoins, vous pouvez le faire avec Net::HTTP
. Une publication de formulaire en plusieurs parties est juste une chaîne soigneusement formatée avec quelques en-têtes supplémentaires. Il semble que chaque programmeur Ruby qui a besoin de publier des articles en plusieurs parties finisse par écrire sa propre petite bibliothèque, ce qui me fait me demander pourquoi cette fonctionnalité n'est pas intégrée. Peut-être que c'est ... Quoi qu'il en soit, pour votre plaisir de lecture, je vais vous donner ma solution ici. Ce code est basé sur des exemples que j'ai trouvés sur quelques blogs, mais je regrette de ne plus pouvoir trouver les liens. Alors je suppose que je dois juste prendre tout le mérite pour moi-même ...
Le module que j'ai écrit pour cela contient une classe publique, pour générer les données de formulaire et les en-têtes à partir d'un hachage d' objets String
et File
. Ainsi, par exemple, si vous souhaitez publier un formulaire avec un paramètre de chaîne nommé "titre" et un paramètre de fichier nommé "document", procédez comme suit:
#prepare the query
data, headers = Multipart::Post.prepare_query("title" => my_string, "document" => my_file)
Ensuite, vous faites juste une 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 quelle que soit la manière dont vous souhaitez effectuer le POST
. Le fait 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 du module Multipart (vous avez besoin du 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