596: def self.build_multipart(params, first = true)
597: if first
598: unless params.is_a?(Hash)
599: raise ArgumentError, "value must be a Hash"
600: end
601:
602: multipart = false
603: query = lambda { |value|
604: case value
605: when Array
606: value.each(&query)
607: when Hash
608: value.values.each(&query)
609: when UploadedFile
610: multipart = true
611: end
612: }
613: params.values.each(&query)
614: return nil unless multipart
615: end
616:
617: flattened_params = Hash.new
618:
619: params.each do |key, value|
620: k = first ? key.to_s : "[#{key}]"
621:
622: case value
623: when Array
624: value.map { |v|
625: build_multipart(v, false).each { |subkey, subvalue|
626: flattened_params["#{k}[]#{subkey}"] = subvalue
627: }
628: }
629: when Hash
630: build_multipart(value, false).each { |subkey, subvalue|
631: flattened_params[k + subkey] = subvalue
632: }
633: else
634: flattened_params[k] = value
635: end
636: end
637:
638: if first
639: flattened_params.map { |name, file|
640: if file.respond_to?(:original_filename)
641: ::File.open(file.path, "rb") do |f|
642: f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding)
643: "--\#{MULTIPART_BOUNDARY}\\r\nContent-Disposition: form-data; name=\"\#{name}\"; filename=\"\#{Utils.escape(file.original_filename)}\"\\r\nContent-Type: \#{file.content_type}\\r\nContent-Length: \#{::File.stat(file.path).size}\\r\n\\r\n\#{f.read}\\r\n"
644: end
645: else
646: "--\#{MULTIPART_BOUNDARY}\\r\nContent-Disposition: form-data; name=\"\#{name}\"\\r\n\\r\n\#{file}\\r\n"
647: end
648: }.join + "--#{MULTIPART_BOUNDARY}--\r"
649: else
650: flattened_params
651: end
652: end