multipart-post-2.4.1/0000755000004100000410000000000014632135600014552 5ustar www-datawww-datamultipart-post-2.4.1/lib/0000755000004100000410000000000014632135600015320 5ustar www-datawww-datamultipart-post-2.4.1/lib/composite_io.rb0000644000004100000410000000120114632135600020330 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2006-2013, by Nick Sieger. # Copyright, 2010, by Tohru Hashimoto. # Copyright, 2011, by Jeff Hodges. # Copyright, 2011, by Alex Koppel. # Copyright, 2011, by Christine Yen. # Copyright, 2011, by Gerrit Riessen. # Copyright, 2011, by Luke Redpath. # Copyright, 2013, by Mislav Marohnić. # Copyright, 2013, by Leo Cassarani. # Copyright, 2019, by Olle Jonsson. # Copyright, 2022-2024, by Samuel Williams. warn "Top level ::CompositeIO is deprecated, require 'multipart/post' and use `Multipart::Post::CompositeReadIO` instead!" require_relative 'multipart/post' multipart-post-2.4.1/lib/parts.rb0000644000004100000410000000156314632135600017003 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2008-2009, by McClain Looney. # Copyright, 2009-2013, by Nick Sieger. # Copyright, 2011, by Johannes Wagener. # Copyright, 2011, by Gerrit Riessen. # Copyright, 2011, by Jason Moore. # Copyright, 2012, by Steven Davidovitz. # Copyright, 2012, by hexfet. # Copyright, 2013, by Vincent Pellé. # Copyright, 2013, by Gustav Ernberg. # Copyright, 2013, by Socrates Vicente. # Copyright, 2017, by David Moles. # Copyright, 2017, by Matt Colyer. # Copyright, 2017, by Eric Hutzelman. # Copyright, 2019, by Olle Jonsson. # Copyright, 2019, by Ethan Turkeltaub. # Copyright, 2022-2024, by Samuel Williams. warn "Top level ::Parts is deprecated, require 'multipart/post' and use `Multipart::Post::Parts` instead!" require_relative 'multipart/post' Parts = Multipart::Post::Parts Object.deprecate_constant :Parts multipart-post-2.4.1/lib/multipart_post.rb0000644000004100000410000000055614632135600020741 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2009-2013, by Nick Sieger. # Copyright, 2019-2024, by Samuel Williams. warn "Top level ::MultipartPost is deprecated, require 'multipart/post' and use `Multipart::Post` instead!" require_relative 'multipart/post' MultipartPost = Multipart::Post Object.deprecate_constant :MultipartPost multipart-post-2.4.1/lib/net/0000755000004100000410000000000014632135600016106 5ustar www-datawww-datamultipart-post-2.4.1/lib/net/http/0000755000004100000410000000000014632135600017065 5ustar www-datawww-datamultipart-post-2.4.1/lib/net/http/post/0000755000004100000410000000000014632135600020052 5ustar www-datawww-datamultipart-post-2.4.1/lib/net/http/post/multipart.rb0000644000004100000410000000110414632135600022414 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2006-2012, by Nick Sieger. # Copyright, 2008, by McClain Looney. # Copyright, 2019, by Olle Jonsson. # Copyright, 2019, by Patrick Davey. # Copyright, 2021-2024, by Samuel Williams. require 'net/http' require_relative '../../../multipart/post' module Net class HTTP class Put class Multipart < Put include ::Multipart::Post::Multipartable end end class Post class Multipart < Post include ::Multipart::Post::Multipartable end end end end multipart-post-2.4.1/lib/multipart/0000755000004100000410000000000014632135600017341 5ustar www-datawww-datamultipart-post-2.4.1/lib/multipart/post.rb0000644000004100000410000000033314632135600020652 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2019, by Patrick Davey. # Copyright, 2021-2024, by Samuel Williams. require_relative 'post/multipartable' require_relative 'post/upload_io' multipart-post-2.4.1/lib/multipart/post/0000755000004100000410000000000014632135600020326 5ustar www-datawww-datamultipart-post-2.4.1/lib/multipart/post/upload_io.rb0000644000004100000410000000443214632135600022631 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2022-2024, by Samuel Williams. module Multipart module Post # Convenience methods for dealing with files and IO that are to be uploaded. class UploadIO attr_reader :content_type, :original_filename, :local_path, :io, :opts # Create an upload IO suitable for including in the params hash of a # Net::HTTP::Post::Multipart. # # Can take two forms. The first accepts a filename and content type, and # opens the file for reading (to be closed by finalizer). # # The second accepts an already-open IO, but also requires a third argument, # the filename from which it was opened (particularly useful/recommended if # uploading directly from a form in a framework, which often save the file to # an arbitrarily named RackMultipart file in /tmp). # # @example # UploadIO.new("file.txt", "text/plain") # UploadIO.new(file_io, "text/plain", "file.txt") def initialize(filename_or_io, content_type, filename = nil, opts = {}) io = filename_or_io local_path = "" if io.respond_to? :read # in Ruby 1.9.2, StringIOs no longer respond to path # (since they respond to :length, so we don't need their local path, see parts.rb:41) local_path = filename_or_io.respond_to?(:path) ? filename_or_io.path : "local.path" else io = File.open(filename_or_io) local_path = filename_or_io end filename ||= local_path @content_type = content_type @original_filename = File.basename(filename) @local_path = local_path @io = io @opts = opts end def self.convert!(io, content_type, original_filename, local_path) raise ArgumentError, "convert! has been removed. You must now wrap IOs " \ "using:\nUploadIO.new(filename_or_io, content_type, " \ "filename=nil)\nPlease update your code." end def method_missing(*args) @io.send(*args) end def respond_to?(meth, include_all = false) @io.respond_to?(meth, include_all) || super(meth, include_all) end end end end UploadIO = Multipart::Post::UploadIO Object.deprecate_constant :UploadIO multipart-post-2.4.1/lib/multipart/post/parts.rb0000644000004100000410000001114414632135600022005 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2008-2009, by McClain Looney. # Copyright, 2009-2013, by Nick Sieger. # Copyright, 2011, by Johannes Wagener. # Copyright, 2011, by Gerrit Riessen. # Copyright, 2011, by Jason Moore. # Copyright, 2012, by Steven Davidovitz. # Copyright, 2012, by hexfet. # Copyright, 2013, by Vincent Pellé. # Copyright, 2013, by Gustav Ernberg. # Copyright, 2013, by Socrates Vicente. # Copyright, 2017, by David Moles. # Copyright, 2017, by Matt Colyer. # Copyright, 2017, by Eric Hutzelman. # Copyright, 2019-2021, by Olle Jonsson. # Copyright, 2019, by Ethan Turkeltaub. # Copyright, 2019, by Patrick Davey. # Copyright, 2021-2024, by Samuel Williams. require 'stringio' module Multipart module Post module Parts module Part def self.new(boundary, name, value, headers = {}) headers ||= {} # avoid nil values if file?(value) FilePart.new(boundary, name, value, headers) else ParamPart.new(boundary, name, value, headers) end end def self.file?(value) value.respond_to?(:content_type) && value.respond_to?(:original_filename) end def length @part.length end def to_io @io end end # Represents a parametric part to be filled with given value. class ParamPart include Part # @param boundary [String] # @param name [#to_s] # @param value [String] # @param headers [Hash] Content-Type and Content-ID are used, if present. def initialize(boundary, name, value, headers = {}) @part = build_part(boundary, name, value, headers) @io = StringIO.new(@part) end def length @part.bytesize end # @param boundary [String] # @param name [#to_s] # @param value [String] # @param headers [Hash] Content-Type is used, if present. def build_part(boundary, name, value, headers = {}) part = String.new part << "--#{boundary}\r\n" part << "Content-ID: #{headers["Content-ID"]}\r\n" if headers["Content-ID"] part << "Content-Disposition: form-data; name=\"#{name.to_s}\"\r\n" part << "Content-Type: #{headers["Content-Type"]}\r\n" if headers["Content-Type"] part << "\r\n" part << "#{value}\r\n" end end # Represents a part to be filled from file IO. class FilePart include Part attr_reader :length # @param boundary [String] # @param name [#to_s] # @param io [IO] # @param headers [Hash] def initialize(boundary, name, io, headers = {}) file_length = io.respond_to?(:length) ? io.length : File.size(io.local_path) @head = build_head(boundary, name, io.original_filename, io.content_type, file_length, io.respond_to?(:opts) ? io.opts.merge(headers) : headers) @foot = "\r\n" @length = @head.bytesize + file_length + @foot.length @io = CompositeReadIO.new(StringIO.new(@head), io, StringIO.new(@foot)) end # @param boundary [String] # @param name [#to_s] # @param filename [String] # @param type [String] # @param content_len [Integer] # @param opts [Hash] def build_head(boundary, name, filename, type, content_len, opts = {}) opts = opts.clone trans_encoding = opts.delete("Content-Transfer-Encoding") || "binary" content_disposition = opts.delete("Content-Disposition") || "form-data" part = String.new part << "--#{boundary}\r\n" part << "Content-Disposition: #{content_disposition}; name=\"#{name.to_s}\"; filename=\"#{filename}\"\r\n" part << "Content-Length: #{content_len}\r\n" if content_id = opts.delete("Content-ID") part << "Content-ID: #{content_id}\r\n" end if opts["Content-Type"] != nil part << "Content-Type: " + opts["Content-Type"] + "\r\n" else part << "Content-Type: #{type}\r\n" end part << "Content-Transfer-Encoding: #{trans_encoding}\r\n" opts.each do |k, v| part << "#{k}: #{v}\r\n" end part << "\r\n" end end # Represents the epilogue or closing boundary. class EpiloguePart include Part def initialize(boundary) @part = String.new("--#{boundary}--\r\n") @io = StringIO.new(@part) end end end end end multipart-post-2.4.1/lib/multipart/post/version.rb0000644000004100000410000000032214632135600022335 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2019, by Patrick Davey. # Copyright, 2021-2024, by Samuel Williams. module Multipart module Post VERSION = "2.4.1" end end multipart-post-2.4.1/lib/multipart/post/multipartable.rb0000644000004100000410000000431614632135600023524 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2008, by McClain Looney. # Copyright, 2008-2013, by Nick Sieger. # Copyright, 2011, by Gerrit Riessen. # Copyright, 2013, by Vincent Pellé. # Copyright, 2013, by Gustav Ernberg. # Copyright, 2013, by Socrates Vicente. # Copyright, 2013, by Steffen Grunwald. # Copyright, 2019, by Olle Jonsson. # Copyright, 2019-2024, by Samuel Williams. # Copyright, 2019, by Patrick Davey. # Copyright, 2022, by Jason York. require_relative 'parts' require_relative 'composite_read_io' require 'securerandom' module Multipart module Post module Multipartable def self.secure_boundary # https://tools.ietf.org/html/rfc7230 # tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" # / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" # / DIGIT / ALPHA # https://tools.ietf.org/html/rfc2046 # bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" / # "+" / "_" / "," / "-" / "." / # "/" / ":" / "=" / "?" "--#{SecureRandom.uuid}" end def initialize(path, params, headers={}, boundary = Multipartable.secure_boundary) headers = headers.clone # don't want to modify the original variable parts_headers = symbolize_keys(headers.delete(:parts) || {}) super(path, headers) parts = symbolize_keys(params).map do |k,v| case v when Array v.map {|item| Parts::Part.new(boundary, k, item, parts_headers[k]) } else Parts::Part.new(boundary, k, v, parts_headers[k]) end end.flatten parts << Parts::EpiloguePart.new(boundary) ios = parts.map {|p| p.to_io } self.set_content_type(headers["Content-Type"] || "multipart/form-data", { "boundary" => boundary }) self.content_length = parts.inject(0) {|sum,i| sum + i.length } self.body_stream = CompositeReadIO.new(*ios) @boundary = boundary end attr :boundary private def symbolize_keys(hash) hash.transform_keys(&:to_sym) end end end end multipart-post-2.4.1/lib/multipart/post/composite_read_io.rb0000644000004100000410000000461214632135600024342 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2006-2013, by Nick Sieger. # Copyright, 2010, by Tohru Hashimoto. # Copyright, 2011, by Jeff Hodges. # Copyright, 2011, by Alex Koppel. # Copyright, 2011, by Christine Yen. # Copyright, 2011, by Gerrit Riessen. # Copyright, 2011, by Luke Redpath. # Copyright, 2013, by Mislav Marohnić. # Copyright, 2013, by Leo Cassarani. # Copyright, 2019, by Olle Jonsson. # Copyright, 2019, by Patrick Davey. # Copyright, 2021, by Lewis Cowles. # Copyright, 2021-2024, by Samuel Williams. module Multipart module Post # Concatenate together multiple IO objects into a single, composite IO object # for purposes of reading as a single stream. # # @example # crio = CompositeReadIO.new(StringIO.new('one'), # StringIO.new('two'), # StringIO.new('three')) # puts crio.read # => "onetwothree" class CompositeReadIO # Create a new composite-read IO from the arguments, all of which should # respond to #read in a manner consistent with IO. def initialize(*ios) @ios = ios.flatten @index = 0 end # Close all the underyling IOs. def close @ios.each do |io| io.close if io.respond_to?(:close) end @ios = nil @index = 0 end def closed? @ios.nil? end # Read from IOs in order until `length` bytes have been received. def read(length = nil, outbuf = nil) if @ios.nil? raise IOError, "CompositeReadIO is closed!" end got_result = false outbuf = outbuf ? outbuf.replace("") : String.new while io = current_io if result = io.read(length) got_result ||= !result.nil? result.force_encoding("BINARY") if result.respond_to?(:force_encoding) outbuf << result length -= result.length if length break if length == 0 end advance_io end (!got_result && length) ? nil : outbuf end def rewind @ios.each { |io| io.rewind } @index = 0 end private def current_io @ios[@index] end def advance_io @index += 1 end end end end CompositeIO = Multipart::Post::CompositeReadIO Object.deprecate_constant :CompositeIO multipart-post-2.4.1/lib/multipartable.rb0000644000004100000410000000127514632135600020517 0ustar www-datawww-data# frozen_string_literal: true # Released under the MIT License. # Copyright, 2008, by McClain Looney. # Copyright, 2008-2013, by Nick Sieger. # Copyright, 2011, by Gerrit Riessen. # Copyright, 2013, by Vincent Pellé. # Copyright, 2013, by Gustav Ernberg. # Copyright, 2013, by Socrates Vicente. # Copyright, 2013, by Steffen Grunwald. # Copyright, 2019, by Olle Jonsson. # Copyright, 2019-2024, by Samuel Williams. # Copyright, 2019, by Patrick Davey. warn "Top level ::Multipartable is deprecated, require 'multipart/post' and use `Multipart::Post::Multipartable` instead!" require_relative 'multipart/post' Multipartable = Multipart::Post::Multipartable Object.deprecate_constant :Multipartable multipart-post-2.4.1/multipart-post.gemspec0000644000004100000410000001013714632135600021125 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: multipart-post 2.4.1 ruby lib Gem::Specification.new do |s| s.name = "multipart-post".freeze s.version = "2.4.1" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.metadata = { "documentation_uri" => "https://socketry.github.io/multipart-post/", "source_code_uri" => "https://github.com/socketry/multipart-post.git" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Nick Sieger".freeze, "Samuel Williams".freeze, "Olle Jonsson".freeze, "McClain Looney".freeze, "Lewis Cowles".freeze, "Gustav Ernberg".freeze, "Patrick Davey".freeze, "Steven Davidovitz".freeze, "Alex Koppel".freeze, "Ethan Turkeltaub".freeze, "Jagtesh Chadha".freeze, "Jason York".freeze, "Tohru Hashimoto".freeze, "Vincent Pell\u00E9".freeze, "hexfet".freeze, "Christine Yen".freeze, "David Moles".freeze, "Eric Hutzelman".freeze, "Feuda Nan".freeze, "Gerrit Riessen".freeze, "Jan Piotrowski".freeze, "Jan-Joost Spanjers".freeze, "Jason Moore".freeze, "Jeff Hodges".freeze, "Johannes Wagener".freeze, "Jordi Massaguer Pla".freeze, "Lachlan Priest".freeze, "Leo Cassarani".freeze, "Lonre Wang".freeze, "Luke Redpath".freeze, "Masato Nakamura".freeze, "Matt Colyer".freeze, "Mislav Marohnic\u0301".freeze, "Peter Goldstein".freeze, "Socrates Vicente".freeze, "Steffen Grunwald".freeze, "Takuya Noguchi".freeze, "Tim Barkley".freeze] s.cert_chain = ["-----BEGIN CERTIFICATE-----\nMIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11\nZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK\nCZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz\nMjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd\nMBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj\nbzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB\nigKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2\n9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW\nsGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE\ne5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN\nXibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss\nRZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn\ntUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM\nzp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW\nxm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O\nBBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs\naWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs\naWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE\ncBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl\nxCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/\nc1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp\n8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws\nJkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP\neX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt\nQ2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8\nvoD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=\n-----END CERTIFICATE-----\n".freeze] s.date = "2024-05-07" s.files = ["changelog.md".freeze, "lib/composite_io.rb".freeze, "lib/multipart/post.rb".freeze, "lib/multipart/post/composite_read_io.rb".freeze, "lib/multipart/post/multipartable.rb".freeze, "lib/multipart/post/parts.rb".freeze, "lib/multipart/post/upload_io.rb".freeze, "lib/multipart/post/version.rb".freeze, "lib/multipart_post.rb".freeze, "lib/multipartable.rb".freeze, "lib/net/http/post/multipart.rb".freeze, "lib/parts.rb".freeze, "license.md".freeze, "readme.md".freeze] s.homepage = "https://github.com/socketry/multipart-post".freeze s.licenses = ["MIT".freeze] s.required_ruby_version = Gem::Requirement.new(">= 2.5.0".freeze) s.rubygems_version = "3.3.15".freeze s.summary = "A multipart form post accessory for Net::HTTP.".freeze end multipart-post-2.4.1/checksums.yaml.gz.sig0000444000004100000410000000060014632135600020615 0ustar www-datawww-data@Fk:\ D kkeڍ$\B4w7ŵbȚst@%x⹡;?ޑ}CȂԠ k8Y+ kh`snxAFr@Q|Ѩ#ǸI;Glۭu%9m^ccC~費cFTp% 6DQGu"dyur ½QRN5m (<g 's.t=$k9.y-ϛ3F)(G1[8aZnra`@HB\jQ#H)U+q N=}jV`@PAK3>ϋBA*{EUmultipart-post-2.4.1/changelog.md0000644000004100000410000000107514632135600017026 0ustar www-datawww-data# CHANGELOG ## Unreleased ### Added - Add the ability to set Content-ID header for ParamPart [#62](https://github.com/socketry/multipart-post/pull/62) - Allow mixed key types for parts headers [#79](https://github.com/socketry/multipart-post/pull/79) ### Changed - Refactor `Parts` into a `Multipart::Post` namespace [#65](https://github.com/socketry/multipart-post/pull/65) - Use mutable strings where needed [#70](https://github.com/socketry/multipart-post/pull/70) - Use `frozen_string_literal` everywhere [#78](https://github.com/socketry/multipart-post/pull/78) multipart-post-2.4.1/readme.md0000644000004100000410000001146314632135600016336 0ustar www-datawww-data# Multipart::Post Adds a streamy multipart form post capability to `Net::HTTP`. Also supports other methods besides `POST`. [![Development Status](https://github.com/socketry/multipart-post/workflows/Test/badge.svg)](https://github.com/socketry/multipart-post/actions?workflow=Test) ## Features/Problems - Appears to actually work. A good feature to have. - Encapsulates posting of file/binary parts and name/value parameter parts, similar to most browsers' file upload forms. - Provides an `UploadIO` helper class to prepare IO objects for inclusion in the params hash of the multipart post object. ## Installation ``` shell bundle add multipart-post ``` ## Usage ``` ruby require 'net/http/post/multipart' url = URI.parse('http://www.example.com/upload') File.open("./image.jpg") do |jpg| req = Net::HTTP::Post::Multipart.new url.path, "file" => UploadIO.new(jpg, "image/jpeg", "image.jpg") res = Net::HTTP.start(url.host, url.port) do |http| http.request(req) end end ``` To post multiple files or attachments, simply include multiple parameters with `UploadIO` values: ``` ruby require 'net/http/post/multipart' url = URI.parse('http://www.example.com/upload') req = Net::HTTP::Post::Multipart.new url.path, "file1" => UploadIO.new(File.new("./image.jpg"), "image/jpeg", "image.jpg"), "file2" => UploadIO.new(File.new("./image2.jpg"), "image/jpeg", "image2.jpg") res = Net::HTTP.start(url.host, url.port) do |http| http.request(req) end ``` To post files with other normal, non-file params such as input values, you need to pass hashes to the `Multipart.new` method. In Rails 4 for example: ``` ruby def model_params require_params = params.require(:model).permit(:param_one, :param_two, :param_three, :avatar) require_params[:avatar] = model_params[:avatar].present? ? UploadIO.new(model_params[:avatar].tempfile, model_params[:avatar].content_type, model_params[:avatar].original_filename) : nil require_params end require 'net/http/post/multipart' url = URI.parse('http://www.example.com/upload') Net::HTTP.start(url.host, url.port) do |http| req = Net::HTTP::Post::Multipart.new(url, model_params) key = "authorization_key" req.add_field("Authorization", key) #add to Headers http.use_ssl = (url.scheme == "https") http.request(req) end ``` Or in plain ruby: ``` ruby def params(file) params = { "description" => "A nice picture!" } params[:datei] = UploadIO.new(file, "image/jpeg", "image.jpg") params end url = URI.parse('http://www.example.com/upload') File.open("./image.jpg") do |file| req = Net::HTTP::Post::Multipart.new(url.path, params(file)) res = Net::HTTP.start(url.host, url.port) do |http| return http.request(req).body end end ``` ### Parts Headers By default, all individual parts will include the header `Content-Disposition` as well as `Content-Length`, `Content-Transfer-Encoding` and `Content-Type` for the File Parts. You may optionally configure the headers `Content-Type` and `Content-ID` for both ParamPart and FilePart by passing in a `parts` header. For example: ``` ruby url = URI.parse('http://www.example.com/upload') params = { "file_metadata_01" => { "description" => "A nice picture!" }, "file_content_01" => UploadIO.new(file, "image/jpeg", "image.jpg") } headers = { 'parts': { 'file_metadata_01': { 'Content-Type' => "application/json" } } } req = Net::HTTP::Post::Multipart.new(uri, params, headers) ``` This would configure the `file_metadata_01` part to include `Content-Type` Content-Disposition: form-data; name="file_metadata_01" Content-Type: application/json { "description" => "A nice picture!" } #### Custom Parts Headers *For FileParts only.* You can include any number of custom parts headers in addition to `Content-Type` and `Content-ID`. ``` ruby headers = { 'parts': { 'file_metadata_01': { 'Content-Type' => "application/json", 'My-Custom-Header' => "Yo Yo!" } } } ``` ### Debugging You can debug requests and responses (e.g. status codes) for all requests by adding the following code: ``` ruby http = Net::HTTP.new(uri.host, uri.port) http.set_debug_output($stdout) ``` ## Versioning This library aims to adhere to [Semantic Versioning 2.0.0](http://semver.org/). Violations of this scheme should be reported as bugs. Specifically, if a minor or patch version is released that breaks backward compatibility, a new version should be immediately released that restores compatibility. Breaking changes to the public API will only be introduced with new major versions. As a result of this policy, you can (and should) specify a dependency on this gem using the [Pessimistic Version Constraint](http://guides.rubygems.org/patterns/#pessimistic-version-constraint) with two digits of precision. For example: ``` ruby spec.add_dependency 'multipart-post', '~> 2.1' ``` multipart-post-2.4.1/data.tar.gz.sig0000444000004100000410000000060014632135600017365 0ustar www-datawww-dataek0x?ɏ1rnCzeX-QeU;LԖ`XKbS1\O|q_&:JȳQ ,JP9-{)'7o,(R=׃`9{vЄod<ګ!@a;0v[vRJ`gOrLuFg O/05tf1sms."%jU8@@܎GP tzcrʵ fu)ǟ|9ZJr*0ܔT%ETоe缷lCGեT)]pBz! I u^C+l/kq4}rS~