views:

14

answers:

1

Using http://github.com/archiloque/rest-client

When posting a file using this line, the content type is set as

Content-Type: multipart/form-data; boundary=301405

in the header, by default.

RestClient.post '/data', :myfile => File.new("/path/to/image.jpg", 'rb')

I tried this and it still sets the header multipart/form-data.

RestClient.post '/data', :myfile => File.new("/path/to/image.jpg", 'rb'), :content_type => 'multipart/related'

Has anybody tried setting multipart/related?

A: 

Ok. I could not find a better alternative, so ended up with the following app.

It is based on this code here. http://stanislavvitvitskiy.blogspot.com/2008/12/multipart-post-in-ruby.html

Usage:

files = { File.new("myfile1","rb"), File.new("myfile2","rb")}
mpost = Mutipart("main", "<my xml main part content which refers to file names>",files )
mpost.post("our_url","post")

Note: This code is not production ready. It is just functional.

---------------- multipart.rb -------------

require 'net/http'
require 'uri'
require 'pp'
require 'mime/types'

class Multipart  

  def initialize( main_part_id, main_part_content, file_names )  
    @file_names = file_names  
    @main_part_id = main_part_id
    @main_part_content = main_part_content
  end

  def post( to_url, method = :post )  
    boundary = "###-------#{Time.new}-----####"

    parts = []  
    streams = []
    # write main part first
    parts << StringPart.new( "--" + boundary + "\r\n")
    parts << StringPart.new("Content-Disposition: name=\"#{@main_part_id}\";\"\r\n" +  
      "Content-ID: #{@main_part_id}\r\n\r\n"+
      "Content-Type: application/xml\r\n\r\n" +  
    @main_part_content + "\r\n\r\n")
    parts << StringPart.new( "\r\n--" + boundary + "\r\n")

    @file_names.each do |param_name, filestream|
      raise 'mutlipartsend: empty file object' if filestream.blank?

      filename= filestream.respond_to?(:original_path) ? filestream.original_path : filestream.path
      ctype = filestream.respond_to?(:content_type) ? filestream.content_type: nil
      fsize = filestream.respond_to?(:lstat) ? filestream.lstat.size : filestream.size


      if !ctype
        begin
            pos = filename.rindex('/') # if filename is a path
            fname = filename[pos + 1, filename.length - pos]
            mm = MIME::Types.type_for(fname)
            ctype = mm.first.content_type if !mm.blank?
        rescue Exception => e
          p e.message
        end
      end
      if !ctype
        ctype= 'application/binary'
        p "mutlipartsend: failed to determine contenttype for #{filename}. using application/binary"
      end


      parts << StringPart.new("Content-Disposition: name=\"" + param_name.to_s + "\"; filename=\"" + filename + "\"\r\n" +  
        "Content-Type: #{ctype}\r\n\r\n")
        #"Content-Type: application/binary\r\n\r\n")
      begin
        stream = File.open(filestream.path,"rb")
        streams << stream 
        parts << StreamPart.new(stream, fsize)
        parts << StringPart.new( "\r\n--" + boundary + "\r\n" )
      rescue Exception => e
        p 'failed to load filestream '+ filestream.path
        p e.message
        raise 'failed to load filestream ' + e.message
      end

    end

    post_stream = MultipartStream.new( parts )  

    url = URI.parse( to_url )
    req = method == :post ? Net::HTTP::Post.new(url.path) : Net::HTTP::Put.new(url.path)
    req.content_length = post_stream.size
    req.content_type = 'multipart/mixed; boundary=' + boundary
    req["myheader1"] = 'header1'
    req["myheader2"] = 'header2'

    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_changed=true
      @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
mv288