curb
có vẻ như là một giải pháp tuyệt vời, nhưng trong trường hợp nó không đáp ứng được nhu cầu của bạn, bạn có thể làm điều đó vớiNet::HTTP
. Bài đăng dạng nhiều phần chỉ là một chuỗi được định dạng cẩn thận với một số tiêu đề bổ sung. Có vẻ như mọi lập trình viên Ruby cần thực hiện các bài viết nhiều phần đều phải viết thư viện nhỏ của riêng họ cho nó, điều này khiến tôi tự hỏi tại sao chức năng này không được tích hợp sẵn. Có lẽ nó là ... Dù sao, vì niềm vui đọc của bạn, tôi sẽ tiếp tục và đưa ra giải pháp của tôi ở đây. Mã này dựa trên các ví dụ tôi tìm thấy trên một vài blog, nhưng tôi rất tiếc vì tôi không thể tìm thấy các liên kết nữa. Vì vậy, tôi đoán tôi chỉ phải nhận tất cả công lao cho bản thân ...
Mô-đun tôi đã viết cho điều này chứa một lớp công khai, để tạo dữ liệu biểu mẫu và tiêu đề từ một băm của String
và File
các đối tượng. Vì vậy, ví dụ: nếu bạn muốn đăng một biểu mẫu có tham số chuỗi có tên "title" và tham số tệp có tên "document", bạn sẽ làm như sau:
#prepare the query
data, headers = Multipart::Post.prepare_query("title" => my_string, "document" => my_file)
Sau đó, bạn chỉ cần làm một điều bình thường POST
với Net::HTTP
:
http = Net::HTTP.new(upload_uri.host, upload_uri.port)
res = http.start {|con| con.post(upload_uri.path, data, headers) }
Hoặc tuy nhiên bạn muốn làm điều đó POST
. Vấn đề là Multipart
trả về dữ liệu và tiêu đề mà bạn cần gửi. Và đó là nó! Đơn giản, phải không? Đây là mã cho mô-đun Multipart (bạn cần mime-types
đá quý):
# 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