S3UpNow
This is a fork of s3_direct_upload.
It's also has code from refile, mainly
related with the frontend.
The general idea of this gem is to be backend agnostic. I'd liked how
s3_direct_upload interact with S3 and I'd liked how refile interact with the
form and the backend, so I made a hybrid.
For now it's only works with paperclip.
Installation
Add this line to your application's Gemfile:
gem 's3-upnow'
Then add a new initalizer with your AWS credentials:
config/initializers/s3-upnow.rb
S3UpNow.config do |c|
c.access_key_id = ""
c.secret_access_key = ""
c.bucket = ""
c.region = nil
c.url = nil
end
Make sure your AWS S3 CORS settings for your bucket look something like this:
<CORSConfiguration>
<CORSRule>
<AllowedOrigin>http://0.0.0.0:3000</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
In production the AllowedOrigin key should be your domain.
Add the following js to your asset pipeline:
application.js.coffee
#= require s3-upnow
Usage
Create a new view that uses the form helper s3_upnow_field
:
= simple_form_for @model do |f|
= f.s3_upnow_field :avatar
= f.button :submit
It will create a file field as well a hidden field with s3_key
suffix. The
code above will generate something like:
<form action="/models" enctype="multipart/form-data" method="post">
<input name="model[avatar_s3_key]" type="hidden">
<input name="model[avatar]" type="file">
</form>
As refile, it will also remove the name attribute of the file field on a
successful upload to S3, so it doesn't get submitted with your form. By the way,
this doesn't play well with remote forms. For now I use the following code to
handle this:
$(document).on('upload:success', 'form', (event) ->
input = $(event.target)
form = input.parents('form').clone()
form.find(input).remove()
$.rails.handleRemote(form)
)
I just have one field, and I want to submit it right after I receive the
upload:success event. I don't know how this will be used in frontends, so I
want to have minimal policies for now. So, be warned and write your own code :)
And that's it! All your models that have has_attached_file :avatar
will copy
the file specified by s3_key to the path expected by paperclip.
Options for form helper
All these options are still used in code, but the callbacks options doesn't have
any effect. I will polish it when I have some tests written.
callback_url:
No default. The url that is POST'd to after file is uploaded to S3. If you don't specify this option, no callback to the server will be made after the file has uploaded to S3.callback_method:
Defaults to POST
. Use PUT and remove the multiple option from your file field to update a model.callback_param:
Defaults to file
. Parameter key for the POST to callback_url
the value will be the full s3 url of the file. If for example this is set to "model[image_url]" then the data posted would be model[image_url] : http://bucketname.s3.amazonws.com/filename.ext
key:
Defaults to uploads/{timestamp}-{unique_id}-#{SecureRandom.hex}/${filename}
. It is the key, or filename used on s3. {timestamp}
and {unique_id}
are special substitution strings that will be populated by javascript with values for the current upload. ${filename}
is a special s3 string that will be populated with the original uploaded file name. Needs to be at least "${filename}"
. It is highly recommended to use both {unique_id}
, which will prevent collisions when uploading files with the same name (such as from a mobile device, where every photo is named image.jpg), and a server-generated random value such as #{SecureRandom.hex}
, which adds further collision protection with other uploaders.key_starts_with:
Defaults to uploads/
. Constraint on the key on s3. if you change the key
option, make sure this starts with what you put there. If you set this as a blank string the upload path to s3 can be anything - not recommended!acl:
Defaults to public-read
. The AWS acl for files uploaded to s3.max_file_size:
Defaults to 500.megabytes
. Maximum file size allowed.id:
Optional html id for the form, its recommended that you give the form an id so you can reference with the jQuery plugin.class:
Optional html class for the form.data:
Optional html data attribute hash.bucket:
Optional (defaults to bucket used in config).
Cleaning old uploads on S3
PS.: I don't test if it's already working, but I intend to do it soon.
You may be processing the files upon upload and reuploading them to another
bucket or directory. If so you can remove the originali files by running a
rake task.
First, add the fog gem to your Gemfile
and run bundle
:
gem 'fog'
Then, run the rake task to delete uploads older than 2 days:
$ rake s3-upnow:clean_remote_uploads
Deleted file with key: "uploads/20121210T2139Z_03846cb0329b6a8eba481ec689135701/06 - PCR_RYA014-25.jpg"
Deleted file with key: "uploads/20121210T2139Z_03846cb0329b6a8eba481ec689135701/05 - PCR_RYA014-24.jpg"
$
Optionally customize the prefix used for cleaning (default is
uploads/#{2.days.ago.strftime('%Y%m%d')}
):
config/initalizers/s3-upnow.rb
S3UpNow.config do |c|
c.prefix_to_clean = "my_path/#{1.week.ago.strftime('%y%m%d')}"
end
Alternately, if you'd prefer for S3 to delete your old uploads automatically,
you can do so by setting your bucket's Lifecycle Configuration.
Contributing / TODO
This is just a simple gem that only really provides some javascript and a form
helper. This gem could go all sorts of ways based on what people want and how
people contribute.
Ideas:
- More specs!
- More options to control file types, ability to batch upload.
- More convention over configuration on frontend side
Credit
This gem is a hybrid of s3_direct_upload
and refile. Maybe it have more personality in
the future.