Uploading photos to Facebook with RFacebook

I’m quite busy recently, and as result I write posts on my blogs with smaller frequency as I usually did… But there are few interesting topics I want to write about. First – RFacebook and photos upload.

I’m using RFacebook gem to handle calls to Facebook API. But recently I found out, that probably there is no way to upload photos to Facebook with RFacebook. The reason is that API call is special case and as far as I know, there is no code for that in RFacebook.

So we need to extend RFacebook by ourself. Problem with facebook.photos.upload is that need to be multi-part encoded post, that means MIME encoding. Since this is one way post, we don’t need any specialized library to handle MIME thus we will prepare own code. To make HTTP request Net::HTTP Ruby library is obvious choice, however it does not support multiparts.

First – Google, and I found this post describing how to make multiparts with Net::HTTP.

Next we need to extend RFacebook::FacebookWebSession providing photos_upload method. RFacebook uses method_missing to handle calls to API, providing own method photos_upload will skip default processing flow:

module RFacebook
  class FacebookWebSession
    
    def photos_upload(params = {})
      params = (params || {}).dup
      params[:method] = "facebook.photos.upload"
      params[:api_key] = FACEBOOK["key"]
      params[:v] = API_VERSION
      params[:session_key] = session_key
      params[:call_id] = Time.now.to_f.to_s
      file = params.delete :file
      params[:sig] = signature(params)
      
      boundary = 'some_long_and_random_string_which_wont_show_in_data_stream'
      params_as_arr = []
      params.each {|k,v| params_as_arr << text_to_multipart(k,v)}
      params_as_arr << file_to_multipart('filename', 'somefile', 'image/jpg', file)
      qry = params_as_arr.collect {|p| 
        '--' + boundary + "\r\n" + p
       }.join('') + "--" + boundary + "--\r\n"
      handle_xml_response(
        Net::HTTP.start(API_HOST).post2(
          API_PATH_REST,
          qry,
          "Content-type" => "multipart/form-data; boundary=" + boundary).response.body.to_s
      )
    end
  end
  
  private
  
    def text_to_multipart(key,value)
      return "Content-Disposition: form-data; name=\"#{key.to_s}\"\r\n" + 
        "\r\n" + 
        "#{value}\r\n"
    end

    def file_to_multipart(key,filename,mime_type,content)
      return "Content-Disposition: form-data; name=\"#{key.to_s}\"; filename=\"#{CGI::escape(filename)}\"\r\n" +
        "Content-Transfer-Encoding: binary\r\n" +
        "Content-Type: #{mime_type}\r\n" + 
        "\r\n" + 
        "#{content}\r\n"
    end

end

Now assuming that fbsession is valid Facebook session we use it providing :file with raw data and :caption for description to Facebook:

fbsession.photos_upload :file => File.read("SOME_PATH_TO_FILE.jpg"),
  :caption => 'some caption'

As You can see this is very ‘rough’ solution, just to give You idea where to head next (error checking, handle other MIME types than image/jpeg, etc).

Please give me You thoughts on that code – if there will be some response I will feel obligated to prepare some more quality code to submit it as patch to RFacebook :)))

Join the Conversation

4 Comments

  1. Hi,
    I am trying this code like this. but I am getting an error
    “can’t dup Symbol
    RAILS_ROOT: E:/Rails2.2/integrateSNS

    Application Trace | Framework Trace | Full Trace
    E:/ruby/lib/ruby/gems/1.8/gems/rfacebook-0.9.8/lib/facebook_session.rb:177:in `dup’
    E:/ruby/lib/ruby/gems/1.8/gems/rfacebook-0.9.8/lib/facebook_session.rb:177:in `remote_call’
    E:/ruby/lib/ruby/gems/1.8/gems/rfacebook-0.9.8/lib/facebook_session.rb:164:in `method_missing’
    E:/ruby/lib/ruby/gems/1.8/gems/rfacebook-0.9.8/lib/facebook_web_session.rb:138:in `photos_upload’
    E:/ruby/lib/ruby/gems/1.8/gems/rfacebook-0.9.8/lib/facebook_web_session.rb:135:in `each’
    E:/ruby/lib/ruby/gems/1.8/gems/rfacebook-0.9.8/lib/facebook_web_session.rb:135:in `photos_uploa

    My code is as follows:
    require “facebook_session”

    module RFacebook

    class FacebookWebSession auth_token})
    unless result.nil?
    @session_user_id = result.at(“uid”).inner_html
    @session_key = result.at(“session_key”).inner_html
    @session_expires = result.at(“expires”).inner_html
    end
    end

    # Sets the session key directly (for example, if you have an infinite session)
    #
    # key:: the session key to use
    def activate_with_previous_session(key, uid=nil, expires=nil)
    # set the expiration
    @session_expires = expires

    # set the session key
    @session_key = key

    # determine the current user’s id
    if uid
    @session_user_id = uid
    else
    result = remote_call(“users.getLoggedInUser”)
    @session_user_id = result.at(“users_getLoggedInUser_response”).inner_html
    end
    end

    # returns true if this session is completely ready to be used and make API calls
    def ready?
    return (@session_key != nil and !expired?)
    end

    # Used for signing a set of parameters in the way that Facebook
    # specifies:
    #
    # params:: a Hash containing the parameters to sign
    def signature(params)
    # always sign the parameters with the API secret
    return signature_helper(params, @api_secret)
    end

    def photos_upload(params = {})
    params = (params || {}).dup
    params[:method] = “facebook.photos.upload”
    params[:api_key] = FACEBOOK[“key”]
    params[:v] = API_VERSION
    params[:session_key] = session_key
    params[:call_id] = Time.now.to_f.to_s
    file = params.delete :file
    params[:sig] = signature(params)
    boundary = ‘some_long_and_random_string_which_wont_show_in_data_stream’
    params_as_arr = []
    p “on 31 line”
    p params
    params.each {|k,v|
    p “<<<<<<<<<<<<<<<<”
    p “text_to_multipart(k,v)”
    p text_to_multipart(k,v)
    params_as_arr << text_to_multipart(k,v)}
    params_as_arr < “multipart/form-data; boundary=” + boundary).response.body.to_s
    )
    end

    end

    def text_to_multipart(key,value)
    p “I aM IN”
    p key
    p value
    return “Content-Disposition: form-data; name=\”#{key.to_s}\”\r\n” +
    “\r\n” +
    “#{value}\r\n”
    end

    def file_to_multipart(key,filename,mime_type,content)
    return “Content-Disposition: form-data; name=\”#{key.to_s}\”; filename=\”#{CGI::escape(filename)}\”\r\n” +
    “Content-Transfer-Encoding: binary\r\n” +
    “Content-Type: #{mime_type}\r\n” +
    “\r\n” +
    “#{content}\r\n”
    end
    end

    I am not able to understand what is happening?
    Can you please tell me what is wrong?

  2. Hi

    I used this code and its working for me.

    Thanks a lot.

    I tried same code with minor changes for video but getting stuck with RemoteStandardError.

    can any one know how to upload video through RFacebook.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.