curb
Sepertinya solusi yang bagus, tetapi jika tidak sesuai dengan kebutuhan Anda, Anda dapat melakukannya dengan Net::HTTP
. Postingan formulir multi bagian hanyalah string yang diformat dengan cermat dengan beberapa header tambahan. Sepertinya setiap programmer Ruby yang perlu melakukan posting multi bagian akhirnya menulis perpustakaan kecil mereka sendiri untuk itu, yang membuat saya bertanya-tanya mengapa fungsi ini tidak ada di dalamnya. Mungkin itu ... Pokoknya, untuk kesenangan membaca Anda, saya akan teruskan dan memberikan solusi saya di sini. Kode ini didasarkan pada contoh yang saya temukan di beberapa blog, tetapi saya menyesal tidak dapat menemukan tautannya lagi. Jadi saya rasa saya harus mengambil semua pujian untuk diri saya sendiri ...
Modul yang saya tulis untuk ini berisi satu kelas publik, untuk menghasilkan data formulir dan header dari hash String
dan File
objek. Jadi misalnya, jika Anda ingin memposting formulir dengan parameter string bernama "title" dan parameter file bernama "document", Anda akan melakukan hal berikut:
#prepare the query
data, headers = Multipart::Post.prepare_query("title" => my_string, "document" => my_file)
Kemudian Anda hanya melakukan yang normal POST
dengan Net::HTTP
:
http = Net::HTTP.new(upload_uri.host, upload_uri.port)
res = http.start {|con| con.post(upload_uri.path, data, headers) }
Atau bagaimanapun Anda ingin melakukan POST
. Intinya adalah Multipart
mengembalikan data dan header yang perlu Anda kirim. Dan itu dia! Sederhana bukan? Berikut kode untuk modul Multipart (Anda membutuhkan mime-types
permata):
# 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