S3 File Field

jQuery File Upload extension for direct uploading to Amazon S3 using CORS

Works as an extension of jQuery File Upload JavaScript plugin and supports IE 7-10.

Note: Since 1.0.2 gem works with both Rails 3 and Rails 4.

Installation

Add this line to your application's Gemfile:

gem 's3_file_field'

Then add a new initalizer with your AWS credentials:

config/initalizers/s3_file_field.rb

S3FileField.config do |c|
  c.access_key_id = ENV['AWS_ACCESS_KEY_ID']
  c.secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
  c.bucket = ENV['AWS_BUCKET']
end

Make sure your AWS S3 CORS Settings for your bucket look like this:

<CORSConfiguration>
  <CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedHeader>*</AllowedHeader>
  </CORSRule>
</CORSConfiguration>

In production the AllowedOrigin key should be your base url like http://example.com

Also ensure you've added PutObject and PutObjectAcl permission in your bucket policy. Here is mine:

{
  "Version": "2008-10-17",
  "Id": "Policy1372930880859",
  "Statement": [
    {
      "Sid": "Stmt1372930877007",
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": [
        "s3:GetObject",
        "s3:PutObjectAcl",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::BUCKET_NAME/*"
    }
  ]
}

Add the following js to your asset pipeline:

application.js.coffee

#= require s3_file_field

Usage

Create a new view that uses the form helper s3_uploader_form:

= form_for :user do |form|
  = form.s3_file_field :scrapbook, :class => 'js-s3_file_field'
  .progress
    .meter{ :style => "width: 0%" }

Then in your application.js.coffee something like:

jQuery.ready ->
  $('.js-s3_file_field').S3FileField()

or

ready = ->
  $(".js-s3_file_field").each ->
    id = $(this).attr('id')
    $this = -> $("##{id}")
    $progress = $(this).siblings('.progress').hide()
    $meter = $progress.find('.meter')
    $(this).S3FileField
      add: (e, data) ->
        $progress.show()
        data.submit()
      done: (e, data) ->
        $progress.hide()
        $this().attr(type: 'text', value: data.result.url, readonly: true)
      fail: (e, data) ->
        alert(data.failReason)
      progress: (e, data) ->
        progress = parseInt(data.loaded / data.total * 100, 10)
        $meter.css(width: "#{progress}%")

$(document).ready(ready)
$(document).on('page:load', ready)

Notice that after upload you have to re-fetch file field from DOM by its ID. That's becase jQuery File Upload clones it somewhere else and $(this) reference points to the original input.

Advanced customization

You can use any options / API available for jQuery File Upload plugin.

After successful upload, you'll find file data in data.result field:

{
  "filepath": "uploads/v3w3qzcb1d78pvi/something.gif",
  "url": "https://foobar.s3.amazonaws.com/uploads/v3w3qzcb1d78pvi/something.gif",
  "filename": "something.gif",
  "filesize": 184387,
  "filetype": "image\/gif",
  "unique_id": "v3w3qzcb1d78pvi"
}

Cleaning old uploads on S3

Check out this article on Lifecycle Configuration.

Thanks

License

This repository is MIT-licensed. You are awesome.