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