Frage

Ich möchte eine HTTP-POST-das sieht aus wie ein HMTL-form geschrieben, aus einem browser.Insbesondere post-text-Feldern und eine Datei-Feld.

Posting-text-Felder ist einfach, es ist ein Beispiel direkt in das net/http rdocs, aber ich kann nicht herausfinden, wie ein post-Datei zusammen mit ihm.

Net::HTTP sieht nicht wie die beste Idee. Bordstein gut aussehend ist.

War es hilfreich?

Lösung

Ich mag RestClient . Sie kapselt Netz / http mit coolen Features wie Multipart-Formulardaten:

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

Es unterstützt auch Streaming.

gem install rest-client werden Sie beginnen.

Andere Tipps

Ich kann nicht sagen, genug gute Dinge über Nick Sieger des mehrt-post-Bibliothek.

Es fügt Unterstützung für Multipart-Posting direkt zu Net :: HTTP, um Ihren Bedarf zu entfernen über Grenzen oder große Bibliotheken manuell zu befürchten, dass andere Ziele als Ihre eigenen haben.

Hier ist ein kleines Beispiel dafür, wie es aus dem 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

Sie können die Bibliothek überprüfen hier: http://github.com/nicksieger/multipart-post

oder installieren Sie es mit:

$ sudo gem install multipart-post

Wenn Sie über SSL Sie eine Verbindung herstellen müssen, die Verbindung wie folgt starten:

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|

curb sieht aus wie eine große Lösung, aber falls es nicht Ihren Anforderungen entspricht, können Sie können tun es mit Net::HTTP. Eine mehrteiliger Form Post ist nur ein sorgfältig formatierten String mit einigen zusätzlichen Header. Es scheint wie jeder Ruby-Programmierer, der mehren Beiträge tun muss, endet für sich ihre eigene kleine Bibliothek zu schreiben, das macht mich fragen, warum diese Funktionalität nicht eingebaut ist. Vielleicht ist es ... Wie auch immer, für Ihr Lesevergnügen, werde ich gehen Sie vor und geben hier meine Lösung. Dieser Code basiert weg von Beispielen, die ich auf ein paar Blogs gefunden, aber ich bedaure, dass ich nicht mehr die Links finden. Also ich denke, ich habe nur für mich den ganzen Kredit nehmen ...

Das Modul I für diese schrieb enthält eine öffentliche Klasse, zum Erzeugen der Formulardaten und Header aus einem Hash von String und File Objekten. So zum Beispiel, wenn Sie ein Formular mit einem String-Parameter namens „Titel“ und eine Datei Parameter mit dem Namen „Dokument“ veröffentlichen wollte, würden Sie wie folgt vor:

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

Dann tun Sie nur einen normalen POST mit Net::HTTP:

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

Oder aber Sie sonst die POST tun wollen. Der Punkt ist, dass Multipart die Daten und Header zurückgibt, die Sie senden müssen. Und das ist es! Einfach, nicht wahr? Hier ist der Code für den Multipart-Modul (Sie müssen das mime-types gem):

# 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

Hier ist meine Lösung nach anderen diejenigen, die versuchen, auf diesen Beitrag vorhanden, ich verwende es Foto auf TwitPic hochladen:

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

Ein anderer nur mit Standardbibliotheken:

uri = 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.open() in case of local file

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

viele Ansätze ausprobiert, aber nur das war für mich gearbeitet.

Ok, hier ist ein einfaches Beispiel mit Kandare.

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]

Schneller Vorlauf bis 2017, ruby stdlib net/http hat dies gebaut-in seit 1.9.3

Net::HTTPRequest#set_form):Hinzugefügt, um Unterstützung sowohl application/x-www-form-urlencoded und multipart/form-data.

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

Wir können sogar mit IO die nicht-Unterstützung :size stream die form-Daten.

In der Hoffnung, dass diese Antwort kann wirklich jemandem helfen :)

P. S.Ich habe nur getestet, die in ruby-2.3.1

restclient funktionierte nicht für mich, bis ich create_file_field in RestClient overrode :: Payload :: Mehrteiliger.

Es wurde eine erstellen: in jedem Teil 'Content-Disposition multipart / form-data' , wo es sollte seine 'Content-Disposition: form-data'.

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

Meine Gabel ist hier, wenn Sie es brauchen: git@github.com: kcrawford / rest-client.git

Nun, die Lösung mit NetHttp hat den Nachteil, dass, wenn große Dateien veröffentlichen es die gesamte Datei in den Speicher lädt zuerst.

Nach einem wenig mit ihm zu spielen kam ich mit folgenden Lösung:

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

gibt es auch nick Siegers mehrt-post auf die lange Liste der möglichen Lösungen hinzuzufügen.

Ich hatte das gleiche Problem (müssen schreiben Web-Server JBoss). Curb funktioniert gut für mich, mit der Ausnahme, dass es verursachte Rubin zum Absturz zu bringen (Rubin 1.8.7 auf Ubuntu 8.10), wenn ich Session-Variablen im Code verwenden.

ich graben sich in den Rest-Client-docs, nicht Anzeige mehrt Unterstützung finden konnten. Ich habe versucht, die Rest-Client Beispiele oben, aber Jboss sagte die http Post ist nicht multipart.

Der mehrt-post gem funktioniert recht gut mit Rails 4 Net :: HTTP, keine andere spezielle gem

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

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top