Upload directly to S3

alexpls edited this page May 9, 2014 · 14 revisions

Here is a rails-specific example of uploading directly to S3 using the newest version of the plugin.

It makes a lot of assumptions, but it will get you where you need to be!

Before starting, you'll need to make sure that you include at least these Javascripts from the File-Upload source:

  • jquery.ui.widget
  • jquery.iframe-transport
  • jquery.fileupload

Ok, given you have a form like so:

%form#file_upload(action="https://YOURBUCKET.s3.amazonaws.com" method="post" enctype="multipart/form-data")
  -# order is important!
  -# also, the things that are not filled in right now *will* be filled in soon.  See below.
  %input{:type => :hidden, :name => :key}
  %input{:type => :hidden, :name => "AWSAccessKeyId", :value => "YOUR_ACCESS_KEY"}
  %input{:type => :hidden, :name => :acl,  :value => :private}
  %input{:type => :hidden, :name => :success_action_status, :value => "200"}
  %input{:type => :hidden, :name => :policy}
  %input{:type => :hidden, :name => :signature}

  .fileupload-content
    .fileupload-progress
  .file-upload
    %label.fileinput-button
      %span Upload Document
      %input{:type => :file, :name => :file}

..and you have javascript to respond to that form like so:

$(function() {
  $('#file_upload').fileupload({
    forceIframeTransport: true,    // VERY IMPORTANT.  you will get 405 Method Not Allowed if you don't add this.
    autoUpload: true,
    add: function (event, data) {
      $.ajax({
        url: "/documents",
        type: 'POST',
        dataType: 'json',
        data: {doc: {title: data.files[0].name}},
        async: false,
        success: function(retdata) {
          // after we created our document in rails, it is going to send back JSON of they key,
          // policy, and signature.  We will put these into our form before it gets submitted to amazon.
          $('#file_upload').find('input[name=key]').val(retdata.key);
          $('#file_upload').find('input[name=policy]').val(retdata.policy);
          $('#file_upload').find('input[name=signature]').val(retdata.signature);
        }
        
      });

      data.submit();
    },
    send: function(e, data) {
      // show a loading spinner because now the form will be submitted to amazon, 
      // and the file will be directly uploaded there, via an iframe in the background. 
      $('#loading').show();
    },
    fail: function(e, data) {
      console.log('fail');
      console.log(data);
    },
    done: function (event, data) {
      // here you can perform an ajax call to get your documents to display on the screen.
      $('#your_documents').load("/documents?for_item=1234");
      
      // hide the loading spinner that we turned on earlier.
      $('#loading').hide();
    },
  });
});

and your controller looks something like this:

  # create the document in rails, then send json back to our javascript to populate the form that will be
  # going to amazon.
  def create
    @document = Document.create(params[:doc])
    render :json => {
      :policy => s3_upload_policy_document, 
      :signature => s3_upload_signature, 
      :key => @document.s3_key, 
      :success_action_redirect => document_upload_success_document_url(@document)
    }
  end
  
  # just in case you need to do anything after the document gets uploaded to amazon.
  # but since we are sending our docs via a hidden iframe, we don't need to show the user a 
  # thank-you page.
  def s3_confirm
    head :ok
  end
 
  private
  
  # generate the policy document that amazon is expecting.
  def s3_upload_policy_document
    return @policy if @policy
    ret = {"expiration" => 5.minutes.from_now.utc.xmlschema,
      "conditions" =>  [ 
        {"bucket" =>  YOUR_BUCKET_NAME}, 
        ["starts-with", "$key", @document.s3_key],
        {"acl" => "private"},
        {"success_action_status" => "200"},
        ["content-length-range", 0, 1048576]
      ]
    }
    @policy = Base64.encode64(ret.to_json).gsub(/\n/,'')
  end

  # sign our request by Base64 encoding the policy document.
  def s3_upload_signature
    signature = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha1'), YOUR_SECRET_KEY, s3_upload_policy_document)).gsub("\n","")
  end
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.