rails net ruby http post

ruby - net - rails send json request



Ruby: ¿Cómo publicar un archivo a través de HTTP como multipart/form-data? (12)

Aquí está mi solución después de probar otras disponibles en esta publicación, la estoy usando para subir fotos en TwitPic:

def upload(photo) `curl -F media=@#{photo.path} -F username=#{@username} -F password=#{@password} -F message=''#{photo.title}'' http://twitpic.com/api/uploadAndPost` end

Quiero hacer un HTTP POST que se parece a un formulario HMTL publicado desde un navegador. Específicamente, publique algunos campos de texto y un campo de archivo.

Publicar campos de texto es sencillo, hay un ejemplo allí mismo en la red / http rdocs, pero no puedo encontrar la manera de publicar un archivo junto con él.

Net :: HTTP no parece ser la mejor idea. curb se ve bien


Avance rápido hasta 2017, ruby stdlib net/http tiene esto incorporado desde 1.9.3

Net :: HTTPRequest # set_form): Se agregó para admitir aplicaciones / x-www-form-urlencoded y multipart / form-data.

https://ruby-doc.org/stdlib-2.3.1/libdoc/net/http/rdoc/Net/HTTPHeader.html#method-i-set_form

Incluso podemos usar IO que no admite :size para transmitir los datos del formulario.

Esperando que esta respuesta realmente pueda ayudar a alguien :)

PD: solo probé esto en ruby ​​2.3.1


Bueno, la solución con NetHttp tiene un inconveniente: cuando publica grandes archivos, primero carga todo el archivo en la memoria.

Después de jugar un poco con esto, se me ocurrió la siguiente solución:

class Multipart def initialize( file_names ) @file_names = file_names end def post( to_url ) boundary = ''----RubyMultipartClient'' + rand(1000000).to_s + ''ZZZZZ'' parts = [] streams = [] @file_names.each do |param_name, filepath| pos = filepath.rindex(''/'') filename = filepath[pos + 1, filepath.length - pos] parts << StringPart.new ( "--" + boundary + "/r/n" + "Content-Disposition: form-data; name=/"" + param_name.to_s + "/"; filename=/"" + filename + "/"/r/n" + "Content-Type: video/x-msvideo/r/n/r/n") stream = File.open(filepath, "rb") streams << stream parts << StreamPart.new (stream, File.size(filepath)) end parts << StringPart.new ( "/r/n--" + boundary + "--/r/n" ) post_stream = MultipartStream.new( parts ) url = URI.parse( to_url ) req = Net::HTTP::Post.new(url.path) req.content_length = post_stream.size req.content_type = ''multipart/form-data; boundary='' + boundary req.body_stream = post_stream res = Net::HTTP.new(url.host, url.port).start {|http| http.request(req) } streams.each do |stream| stream.close(); end res end end class StreamPart def initialize( stream, size ) @stream, @size = stream, size end def size @size end def read ( offset, how_much ) @stream.read ( how_much ) end end class StringPart def initialize ( str ) @str = str end def size @str.length end def read ( offset, how_much ) @str[offset, how_much] end end class MultipartStream def initialize( parts ) @parts = parts @part_no = 0; @part_offset = 0; end def size total = 0 @parts.each do |part| total += part.size end total end def read ( how_much ) if @part_no >= @parts.size return nil; end how_much_current_part = @parts[@part_no].size - @part_offset how_much_current_part = if how_much_current_part > how_much how_much else how_much_current_part end how_much_next_part = how_much - how_much_current_part current_part = @parts[@part_no].read(@part_offset, how_much_current_part ) if how_much_next_part > 0 @part_no += 1 @part_offset = 0 next_part = read ( how_much_next_part ) current_part + if next_part next_part else '''' end else @part_offset += how_much_current_part current_part end end end


La gema multiparte post funciona bastante bien con Rails 4 Net :: HTTP, no hay otra gema especial

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

https://github.com/Feuda/multipart-post/tree/patch-1


Me gusta RestClient . Encapsula net / http con funciones geniales como datos de formulario multiparte:

require ''rest_client'' RestClient.post(''http://localhost:3000/foo'', :name_of_file_param => File.new(''/path/to/file''))

También es compatible con la transmisión.

gem install rest-client ayudará a comenzar.


No puedo decir suficientes cosas buenas sobre la biblioteca de publicaciones múltiples de Nick Sieger.

Agrega soporte para publicación de múltiples partes directamente a Net :: HTTP, lo que elimina la necesidad de preocuparse manualmente por los límites o grandes bibliotecas que pueden tener objetivos diferentes a los suyos.

Aquí hay un pequeño ejemplo sobre cómo usarlo desde el README :

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

Puede consultar la biblioteca aquí: http://github.com/nicksieger/multipart-post

o instalarlo con:

$ sudo gem install multipart-post

Si se conecta mediante SSL, debe iniciar la conexión de esta manera:

n = Net::HTTP.new(url.host, url.port) n.use_ssl = true # for debugging dev server #n.verify_mode = OpenSSL::SSL::VERIFY_NONE res = n.start do |http|


Ok, aquí hay un ejemplo simple usando el bordillo.

require ''yaml'' require ''curb'' # prepare post data post_data = fields_hash.map { |k, v| Curl::PostField.content(k, v.to_s) } post_data << Curl::PostField.file(''file'', ''/path/to/file''), # post c = Curl::Easy.new(''http://localhost:3000/foo'') c.multipart_form_post = true c.http_post(post_data) # print response y [c.response_code, c.body_str]


Otro que usa solo bibliotecas estándar:

uri = ''https://some.end.point/some/path'' request = Net::HTTP::Post.new(uri) request[''Authorization''] = ''If you need some headers'' form_data = [[''photos'', photo.tempfile]] # or File.read() request.set_form form_data, ''multipart/form-data'' response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| # pay attention to use_ssl if you need it http.request(request) end

Intenté muchos enfoques, pero solo esto me funcionó.


Tuve el mismo problema (necesito publicar en el servidor web de jboss). Curb funciona bien para mí, excepto que causó que ruby ​​se bloquee (ruby 1.8.7 en ubuntu 8.10) cuando uso variables de sesión en el código.

Indago en el resto de los documentos del cliente, no pude encontrar indicación de soporte multiparte. Probé los ejemplos de clientes restantes anteriores, pero jboss dijo que la publicación http no es multiparte.


restclient no funcionó para mí hasta que anulé create_file_field en RestClient :: Payload :: Multipart.

Estaba creando una ''Disposición del contenido: multipart / form-data'' en cada parte donde debería estar ''Content-Disposition: form-data'' .

http://www.ietf.org/rfc/rfc2388.txt

Mi tenedor está aquí si lo necesitas: [email protected]: kcrawford / rest-client.git



curb ve como una gran solución, pero en caso de que no satisfaga sus necesidades, puede hacerlo con Net::HTTP . Una publicación de formulario de varias partes es solo una cadena cuidadosamente formateada con algunos encabezados adicionales. Parece que cada programador de Ruby que necesita hacer publicaciones multiparte termina escribiendo su propia pequeña biblioteca, lo que me hace preguntarme por qué esta funcionalidad no está incorporada. Tal vez sea ... De todos modos, para su placer de lectura, seguiré adelante y daré mi solución aquí. Este código se basa en ejemplos que encontré en un par de blogs, pero lamento que ya no pueda encontrarlos. Así que supongo que debo tomar todo el mérito por mí mismo ...

El módulo que escribí para esto contiene una clase pública, para generar los datos del formulario y los encabezados de un hash de objetos String y File . Por ejemplo, si desea publicar un formulario con un parámetro de cadena denominado "título" y un parámetro de archivo llamado "documento", debe hacer lo siguiente:

#prepare the query data, headers = Multipart::Post.prepare_query("title" => my_string, "document" => my_file)

Entonces solo haces un POST normal con Net::HTTP :

http = Net::HTTP.new(upload_uri.host, upload_uri.port) res = http.start {|con| con.post(upload_uri.path, data, headers) }

O como quiera que quiera hacer el POST . El punto es que Multipart devuelve los datos y encabezados que necesita enviar. ¡Y eso es! Simple, ¿verdad? Aquí está el código para el módulo Multipart (necesitas la gema mime-types ):

# 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:[email protected]> # 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