Skip to content

Commit

Permalink
Merge pull request waynehoover#192 from ncri/master
Browse files Browse the repository at this point in the history
Add cleaned_filename replacement variable for key and use uploader inside forms
  • Loading branch information
waynehoover committed Jun 26, 2015
2 parents 47de8f5 + a98dc01 commit ec17a0e
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 33 deletions.
30 changes: 15 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![Build Status](https://travis-ci.org/waynehoover/s3_direct_upload.png)](https://travis-ci.org/waynehoover/s3_direct_upload)

Easily generate a form that allows you to upload directly to Amazon S3.
Easily upload files directly to Amazon S3.
Multi file uploading supported by jquery-fileupload.

Code extracted from Ryan Bates' [gallery-jquery-fileupload](https://github.com/railscasts/383-uploading-to-amazon-s3/tree/master/gallery-jquery-fileupload).
Expand Down Expand Up @@ -53,10 +53,10 @@ Add the following js and css to your asset pipeline:
```

## Usage
Create a new view that uses the form helper `s3_uploader_form`:
Use the `s3_uploader` helper to add an s3 upload file field to your view:
```ruby
<%= s3_uploader_form callback_url: model_url, callback_param: "model[image_url]", id: "s3-uploader" do %>
<%= file_field_tag :file, multiple: true %>
<%= s3_uploader callback_url: model_url, callback_param: "model[image_url]", id: "s3-uploader" do %>
<%= file_field_tag :file, multiple: true, data: { url: s3_uploader_url } %>
<% end %>
```

Expand Down Expand Up @@ -84,7 +84,7 @@ Optionally, you can also place this template in the same view for the progress b
* `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:` Defaults to `uploads/{timestamp}-{unique_id}-#{SecureRandom.hex}/${filename}`. It is the key, or filename used on s3. `{timestamp}`, `{unique_id}`, `{extension}` and `{cleaned_filename}` are special substitution strings that will be populated by javascript with values for the current upload. {cleaned_filename} is the original filename with special characters removed. `${filename}` is a special s3 string that will be populated with the original uploaded file name. Needs to be at least `"${filename}"` or `"${cleaned_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.
Expand All @@ -95,17 +95,17 @@ Optionally, you can also place this template in the same view for the progress b

### Example with all options
```ruby
<%= s3_uploader_form callback_url: model_url,
callback_method: "POST",
callback_param: "model[image_url]",
key: "files/{timestamp}-{unique_id}-#{SecureRandom.hex}/${filename}",
key_starts_with: "files/",
acl: "public-read",
max_file_size: 50.megabytes,
id: "s3-uploader",
class: "upload-form",
<%= s3_uploader_form callback_url: model_url,
callback_method: "POST",
callback_param: "model[image_url]",
key: "files/{timestamp}-{unique_id}-#{SecureRandom.hex}/${filename}",
key_starts_with: "files/",
acl: "public-read",
max_file_size: 50.megabytes,
id: "s3-uploader",
class: "upload-form",
data: {:key => :val} do %>
<%= file_field_tag :file, multiple: true %>
<%= file_field_tag :file, multiple: true, data: { url: s3_uploader_url } %>
<% end %>
```
Expand Down
22 changes: 17 additions & 5 deletions app/assets/javascripts/s3_direct_upload.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,14 @@ $.fn.S3Uploader = (options) ->
form.submit() for form in forms_for_submit
false

$wrapping_form = $uploadForm.closest('form')
if $wrapping_form.length > 0
$wrapping_form.off('submit').on 'submit', ->
$wrapping_form.find('.s3_uploader input').prop "disabled", true
true

setUploadForm = ->
$uploadForm.fileupload
$uploadForm.find("input[type='file']").fileupload

add: (e, data) ->
file = data.files[0]
Expand Down Expand Up @@ -105,7 +111,7 @@ $.fn.S3Uploader = (options) ->
$uploadForm.trigger("s3_upload_failed", [content])

formData: (form) ->
data = form.serializeArray()
data = $uploadForm.find("input").serializeArray()
fileType = ""
if "type" of @files[0]
fileType = @files[0].type
Expand All @@ -116,6 +122,7 @@ $.fn.S3Uploader = (options) ->
key = $uploadForm.data("key")
.replace('{timestamp}', new Date().getTime())
.replace('{unique_id}', @files[0].unique_id)
.replace('{cleaned_filename}', cleaned_filename(@files[0].name))
.replace('{extension}', @files[0].name.split('.').pop())

# substitute upload timestamp and unique_id into key
Expand All @@ -137,9 +144,11 @@ $.fn.S3Uploader = (options) ->
content.url = $(result).find("Location").text()
content.filepath = $('<a />').attr('href', content.url)[0].pathname
else # IE <= 9 retu rn a null result object so we use the file object instead
domain = $uploadForm.attr('action')
content.filepath = $uploadForm.find('input[name=key]').val().replace('/${filename}', '')
content.url = domain + content.filepath + '/' + encodeURIComponent(file.name)
domain = $uploadForm.find('input[type=file]').data('url')
key = $uploadForm.find('input[name=key]').val()
content.filepath = key.replace('/${filename}', '').replace('/{cleaned_filename}', '')
content.url = domain + key.replace('/${filename}', encodeURIComponent(file.name))
content.url = content.url.replace('/{cleaned_filename}', cleaned_filename(file.name))

content.filename = file.name
content.filesize = file.size if 'size' of file
Expand All @@ -150,6 +159,9 @@ $.fn.S3Uploader = (options) ->
content = $.extend content, settings.additional_data if settings.additional_data
content

cleaned_filename = (filename) ->
filename.replace(/\s/g, '_').replace(/[^\w.-]/gi, '')

has_relativePath = (file) ->
file.relativePath || file.webkitRelativePath

Expand Down
19 changes: 8 additions & 11 deletions lib/s3_direct_upload/form_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,26 @@ module S3DirectUpload
module UploadHelper
def s3_uploader_form(options = {}, &block)
uploader = S3Uploader.new(options)
form_tag(uploader.url, uploader.form_options) do
content_tag(:div, uploader.wrapper_options) do
uploader.fields.map do |name, value|
hidden_field_tag(name, value)
end.join.html_safe + capture(&block)
end
end

alias_method :s3_uploader, :s3_uploader_form

def s3_uploader_url ssl = true
S3DirectUpload.config.url || "http#{ssl ? 's' : ''}://#{S3DirectUpload.config.region || "s3"}.amazonaws.com/#{S3DirectUpload.config.bucket}/"
end

class S3Uploader
def initialize(options)
@key_starts_with = options[:key_starts_with] || "uploads/"
@options = options.reverse_merge(
aws_access_key_id: S3DirectUpload.config.access_key_id,
aws_secret_access_key: S3DirectUpload.config.secret_access_key,
bucket: options[:bucket] || S3DirectUpload.config.bucket,
region: S3DirectUpload.config.region || "s3",
url: S3DirectUpload.config.url,
ssl: true,
acl: "public-read",
expiration: 10.hours.from_now.utc.iso8601,
Expand All @@ -29,13 +33,10 @@ def initialize(options)
)
end

def form_options
def wrapper_options
{
id: @options[:id],
class: @options[:class],
method: "post",
authenticity_token: false,
multipart: true,
data: {
callback_url: @options[:callback_url],
callback_method: @options[:callback_method],
Expand All @@ -60,10 +61,6 @@ def key
@key ||= "#{@key_starts_with}{timestamp}-{unique_id}-#{SecureRandom.hex}/${filename}"
end

def url
@options[:url] || "http#{@options[:ssl] ? 's' : ''}://#{@options[:region]}.amazonaws.com/#{@options[:bucket]}/"
end

def policy
Base64.encode64(policy_data.to_json).gsub("\n", "")
end
Expand Down
4 changes: 2 additions & 2 deletions spec/existance_spec.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
require 'spec_helper'
describe S3DirectUpload do
it "version must be defined" do
S3DirectUpload::VERSION.should be_true
S3DirectUpload::VERSION.should be_present
end

it "config must be defined" do
S3DirectUpload.config.should be_true
S3DirectUpload.config.should be_present
end

end

0 comments on commit ec17a0e

Please sign in to comment.