s3_cors_fileupload 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +51 -0
- data/.rspec +1 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +39 -0
- data/LICENSE.txt +20 -0
- data/README.md +104 -0
- data/Rakefile +58 -0
- data/lib/generators/s3_cors_fileupload/install/USAGE +17 -0
- data/lib/generators/s3_cors_fileupload/install/install_generator.rb +51 -0
- data/lib/generators/s3_cors_fileupload/install/templates/amazon_s3.yml +17 -0
- data/lib/generators/s3_cors_fileupload/install/templates/create_source_files.rb +14 -0
- data/lib/generators/s3_cors_fileupload/install/templates/s3_uploads.js +94 -0
- data/lib/generators/s3_cors_fileupload/install/templates/s3_uploads_controller.rb +90 -0
- data/lib/generators/s3_cors_fileupload/install/templates/source_file.rb +53 -0
- data/lib/generators/s3_cors_fileupload/install/templates/views/_template_download.html.erb +29 -0
- data/lib/generators/s3_cors_fileupload/install/templates/views/_template_upload.html.erb +31 -0
- data/lib/generators/s3_cors_fileupload/install/templates/views/_template_uploaded.html.erb +25 -0
- data/lib/generators/s3_cors_fileupload/install/templates/views/index.html.erb +43 -0
- data/lib/s3_cors_fileupload.rb +2 -0
- data/lib/s3_cors_fileupload/rails.rb +8 -0
- data/lib/s3_cors_fileupload/rails/config.rb +27 -0
- data/lib/s3_cors_fileupload/rails/engine.rb +6 -0
- data/lib/s3_cors_fileupload/rails/form_helper.rb +91 -0
- data/lib/s3_cors_fileupload/rails/policy_helper.rb +48 -0
- data/lib/s3_cors_fileupload/version.rb +5 -0
- data/s3_cors_fileupload.gemspec +35 -0
- data/spec/s3_cors_fileupload/version_spec.rb +17 -0
- data/spec/s3_cors_fileupload_spec.rb +9 -0
- data/spec/spec_helper.rb +16 -0
- data/vendor/assets/images/loading.gif +0 -0
- data/vendor/assets/images/progressbar.gif +0 -0
- data/vendor/assets/javascripts/s3_cors_fileupload/index.js +6 -0
- data/vendor/assets/javascripts/s3_cors_fileupload/jquery.fileupload-ui.js +732 -0
- data/vendor/assets/javascripts/s3_cors_fileupload/jquery.fileupload.js +1106 -0
- data/vendor/assets/javascripts/s3_cors_fileupload/jquery.iframe-transport.js +172 -0
- data/vendor/assets/javascripts/s3_cors_fileupload/vendor/jquery.ui.widget.js +511 -0
- data/vendor/assets/javascripts/s3_cors_fileupload/vendor/load-image.js +122 -0
- data/vendor/assets/javascripts/s3_cors_fileupload/vendor/tmpl.js +87 -0
- data/vendor/assets/stylesheets/jquery.fileupload-ui.css.erb +85 -0
- metadata +205 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'openssl'
|
3
|
+
require 'digest/sha1'
|
4
|
+
|
5
|
+
class S3UploadsController < ApplicationController
|
6
|
+
|
7
|
+
helper_method :s3_upload_policy_document, :s3_upload_signature
|
8
|
+
|
9
|
+
# GET /source_files
|
10
|
+
# GET /source_files.json
|
11
|
+
def index
|
12
|
+
@source_files = SourceFile.all
|
13
|
+
|
14
|
+
respond_to do |format|
|
15
|
+
format.html # index.html.erb
|
16
|
+
format.json { render json: @source_files.map{|sf| sf.to_jq_upload } }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# POST /source_files
|
21
|
+
# POST /source_files.json
|
22
|
+
def create
|
23
|
+
@source_file = SourceFile.new(params[:source_file])
|
24
|
+
respond_to do |format|
|
25
|
+
if @source_file.save
|
26
|
+
format.html {
|
27
|
+
render :json => @source_file.to_jq_upload,
|
28
|
+
:content_type => 'text/html',
|
29
|
+
:layout => false
|
30
|
+
}
|
31
|
+
format.json { render json: @source_file.to_jq_upload, status: :created }
|
32
|
+
else
|
33
|
+
format.html { render action: "new" }
|
34
|
+
format.json { render json: @source_file.errors, status: :unprocessable_entity }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# DELETE /source_files/1
|
40
|
+
# DELETE /source_files/1.json
|
41
|
+
def destroy
|
42
|
+
@source_file = SourceFile.find(params[:id])
|
43
|
+
@source_file.destroy
|
44
|
+
|
45
|
+
respond_to do |format|
|
46
|
+
format.html { redirect_to source_files_url }
|
47
|
+
format.json { head :no_content }
|
48
|
+
format.xml { head :no_content }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# used for s3_uploader
|
53
|
+
def generate_key
|
54
|
+
uid = SecureRandom.uuid.gsub(/-/,'')
|
55
|
+
|
56
|
+
render json: {
|
57
|
+
key: "uploads/#{uid}/#{params[:filename]}",
|
58
|
+
success_action_redirect: "/"
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
# ---- Helpers ----
|
63
|
+
# generate the policy document that amazon is expecting.
|
64
|
+
def s3_upload_policy_document
|
65
|
+
Base64.encode64(
|
66
|
+
{
|
67
|
+
expiration: 1.hour.from_now.utc.strftime('%Y-%m-%dT%H:%M:%S.000Z'),
|
68
|
+
conditions: [
|
69
|
+
{ bucket: S3CorsFileupload::Config.bucket },
|
70
|
+
{ acl: 'public-read' },
|
71
|
+
{ success_action_status: '201' },
|
72
|
+
["starts-with", "$key", ""],
|
73
|
+
["starts-with", "$Content-Type", ""]
|
74
|
+
]
|
75
|
+
}.to_json
|
76
|
+
).gsub(/\n|\r/, '')
|
77
|
+
end
|
78
|
+
|
79
|
+
# sign our request by Base64 encoding the policy document.
|
80
|
+
def s3_upload_signature
|
81
|
+
Base64.encode64(
|
82
|
+
OpenSSL::HMAC.digest(
|
83
|
+
OpenSSL::Digest::Digest.new('sha1'),
|
84
|
+
S3CorsFileupload::Config.secret_access_key,
|
85
|
+
s3_upload_policy_document
|
86
|
+
)
|
87
|
+
).gsub(/\n/, '')
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'aws/s3'
|
2
|
+
|
3
|
+
class SourceFile < ActiveRecord::Base
|
4
|
+
attr_accessible :url, :bucket, :key
|
5
|
+
|
6
|
+
validates_presence_of :file_name, :file_content_type, :file_size, :key, :bucket
|
7
|
+
|
8
|
+
before_validation(:on => :create) do
|
9
|
+
self.file_name = key.split('/').last if key
|
10
|
+
# for some reason, the response from AWS seems to escape the slashes in the keys, this line will unescape the slash
|
11
|
+
self.url = url.gsub(/%2F/, '/') if url
|
12
|
+
self.file_size ||= s3_object.try(:size)
|
13
|
+
self.file_content_type ||= s3_object.try(:content_type)
|
14
|
+
end
|
15
|
+
# make all attributes readonly after creating the record (not sure we need this?)
|
16
|
+
after_create { readonly! }
|
17
|
+
# cleanup; destroy corresponding file on S3
|
18
|
+
after_destroy { s3_object.try(:delete) }
|
19
|
+
|
20
|
+
def to_jq_upload
|
21
|
+
{
|
22
|
+
'id' => id,
|
23
|
+
'name' => file_name,
|
24
|
+
'size' => file_size,
|
25
|
+
'url' => url,
|
26
|
+
'image' => self.is_image?,
|
27
|
+
'delete_url' => Rails.application.routes.url_helpers.source_file_path(self)
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def is_image?
|
32
|
+
!!file_content_type.try(:match, /image/)
|
33
|
+
end
|
34
|
+
|
35
|
+
#---- start S3 related methods -----
|
36
|
+
def s3_object
|
37
|
+
@s3_object ||= AWS::S3::S3Object.find(key, bucket) if self.class.open_aws && key
|
38
|
+
rescue
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.open_aws
|
43
|
+
unless AWS::S3::Base.connected?
|
44
|
+
AWS::S3::Base.establish_connection!(
|
45
|
+
:access_key_id => S3CorsFileupload::Config.access_key_id,
|
46
|
+
:secret_access_key => S3CorsFileupload::Config.secret_access_key
|
47
|
+
)
|
48
|
+
end
|
49
|
+
return AWS::S3::Base.connected?
|
50
|
+
end
|
51
|
+
#---- end S3 related methods -----
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<!-- The template to display files available for download -->
|
2
|
+
<script id="template-download" type="text/x-tmpl">
|
3
|
+
{% for (var i=0, file; file=o.files[i]; i++) { %}
|
4
|
+
<tr class="template-download fade">
|
5
|
+
{% if (file.error) { %}
|
6
|
+
<td></td>
|
7
|
+
<td class="name"><span>{%=file.name%}</span></td>
|
8
|
+
<td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
|
9
|
+
<td class="error" colspan="2"><span class="label label-important">Error</span> {%=file.error%}</td>
|
10
|
+
{% } else { %}
|
11
|
+
<td class="preview">{% if (file.thumbnail_url) { %}
|
12
|
+
<a href="{%=file.url%}" title="{%=file.name%}" rel="gallery" download="{%=file.name%}"><img src="{%=file.thumbnail_url%}"></a>
|
13
|
+
{% } %}</td>
|
14
|
+
<td class="name">
|
15
|
+
<a href="{%=file.url%}" title="{%=file.name%}" rel="{%=file.thumbnail_url&&'gallery'%}" download="{%=file.name%}">{%=file.name%}</a>
|
16
|
+
</td>
|
17
|
+
<td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
|
18
|
+
<td colspan="2"></td>
|
19
|
+
{% } %}
|
20
|
+
<td class="delete">
|
21
|
+
<button class="btn btn-danger" data-type="{%=file.delete_type%}" data-url="{%=file.delete_url%}">
|
22
|
+
<i class="icon-trash icon-white"></i>
|
23
|
+
<span>Delete</span>
|
24
|
+
</button>
|
25
|
+
<input type="checkbox" name="delete" value="1">
|
26
|
+
</td>
|
27
|
+
</tr>
|
28
|
+
{% } %}
|
29
|
+
</script>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<!-- The template to display files available for upload -->
|
2
|
+
<script id="template-upload" type="text/x-tmpl">
|
3
|
+
{% for (var i=0, file; file=o.files[i]; i++) { %}
|
4
|
+
<tr class="template-upload fade">
|
5
|
+
<td class="preview"><span class="fade"></span></td>
|
6
|
+
<td class="name"><span>{%=file.name%}</span></td>
|
7
|
+
<td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
|
8
|
+
{% if (file.error) { %}
|
9
|
+
<td class="error" colspan="2"><span class="label label-important">Error</span> {%=file.error%}</td>
|
10
|
+
{% } else if (o.files.valid && !i) { %}
|
11
|
+
<td>
|
12
|
+
<div class="progress progress-success progress-striped active"><div class="bar" style="width:0%;"></div></div>
|
13
|
+
</td>
|
14
|
+
<td class="start">{% if (!o.options.autoUpload) { %}
|
15
|
+
<button class="btn btn-primary">
|
16
|
+
<i class="icon-upload icon-white"></i>
|
17
|
+
<span>Start</span>
|
18
|
+
</button>
|
19
|
+
{% } %}</td>
|
20
|
+
{% } else { %}
|
21
|
+
<td colspan="2"></td>
|
22
|
+
{% } %}
|
23
|
+
<td class="cancel">{% if (!i) { %}
|
24
|
+
<button class="btn btn-warning">
|
25
|
+
<i class="icon-ban-circle icon-white"></i>
|
26
|
+
<span>Cancel</span>
|
27
|
+
</button>
|
28
|
+
{% } %}</td>
|
29
|
+
</tr>
|
30
|
+
{% } %}
|
31
|
+
</script>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<!-- The template to display files that have been uploaded to S3 already -->
|
2
|
+
<!-- This essentially is a stand-in for the template_download partial for the s3_cors_fileupload gem's purposes -->
|
3
|
+
<script id="template-uploaded" type="text/x-tmpl">
|
4
|
+
<tr class="template-uploaded" id="source_file_{%=o.id%}">
|
5
|
+
<td class="preview">
|
6
|
+
{% if (o.image == true) { %}
|
7
|
+
<a href="{%=o.url%}" title="{%=o.name%}" rel="gallery" download="{%=o.name%}">
|
8
|
+
<image src="{%=o.url%}", style='width:80px; height:56px;'></image>
|
9
|
+
</a>
|
10
|
+
{% } %}
|
11
|
+
</td>
|
12
|
+
<td class="name">
|
13
|
+
<a href="{%=o.url%}" title="{%=o.name%}" rel="{%=o.thumbnail_url&&'gallery'%}" download="{%=o.name%}">{%=o.name%}</a>
|
14
|
+
</td>
|
15
|
+
<td class="size"><span>{%=formatFileSize(o.size)%}</span></td>
|
16
|
+
<td colspan="2"></td>
|
17
|
+
<td class="delete">
|
18
|
+
<button class="btn btn-danger" data-type="DELETE" data-url="{%=o.delete_url%}">
|
19
|
+
<i class="icon-trash icon-white"></i>
|
20
|
+
<span>Delete</span>
|
21
|
+
</button>
|
22
|
+
<input type="checkbox" name="delete" value="1">
|
23
|
+
</td>
|
24
|
+
</tr>
|
25
|
+
</script>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<%= stylesheet_link_tag '//netdna.bootstrapcdn.com/twitter-bootstrap/2.1.1/css/bootstrap-combined.min.css' %>
|
2
|
+
|
3
|
+
<div style="padding: 10px;">
|
4
|
+
<h2>Upload file</h2>
|
5
|
+
<%= s3_cors_fileupload_form :id => 'fileupload' %>
|
6
|
+
|
7
|
+
<div class="javascript-templates">
|
8
|
+
<%= render 'template_upload' %>
|
9
|
+
<%= render 'template_uploaded' %>
|
10
|
+
<%# render 'template_download' %>
|
11
|
+
</div>
|
12
|
+
</div>
|
13
|
+
|
14
|
+
<script>
|
15
|
+
//<![CDATA[
|
16
|
+
$(function () {
|
17
|
+
// Initialize the jQuery File Upload widget:
|
18
|
+
$('#fileupload').fileupload({
|
19
|
+
dataType: 'xml',
|
20
|
+
sequentialUploads: true,
|
21
|
+
downloadTemplateId: null,
|
22
|
+
downloadTemplate: null
|
23
|
+
});
|
24
|
+
|
25
|
+
// Load existing files:
|
26
|
+
$.getJSON('/source_files', function (files) {
|
27
|
+
$.each(files, function(index, value) {
|
28
|
+
$('#upload_files tbody').append(tmpl('template-uploaded', value));
|
29
|
+
});
|
30
|
+
});
|
31
|
+
});
|
32
|
+
|
33
|
+
// used by the jQuery File Upload
|
34
|
+
var fileUploadErrors = {
|
35
|
+
maxFileSize: 'File is too big',
|
36
|
+
minFileSize: 'File is too small',
|
37
|
+
acceptFileTypes: 'Filetype not allowed',
|
38
|
+
maxNumberOfFiles: 'Max number of files exceeded',
|
39
|
+
uploadedBytes: 'Uploaded bytes exceed file size',
|
40
|
+
emptyResult: 'Empty file upload result'
|
41
|
+
};
|
42
|
+
//]]>
|
43
|
+
</script>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
if defined?(::Rails)
|
2
|
+
require 's3_cors_fileupload/rails/config'
|
3
|
+
require 's3_cors_fileupload/rails/engine' if ::Rails.version >= '3.1'
|
4
|
+
require 's3_cors_fileupload/rails/policy_helper'
|
5
|
+
require 's3_cors_fileupload/rails/form_helper'
|
6
|
+
|
7
|
+
ActionView::Base.send(:include, S3CorsFileupload::FormHelper) if defined?(ActionView::Base)
|
8
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module S3CorsFileupload
|
2
|
+
module Config
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
# this allows us to lazily instantiate the configuration by reading it in when it needs to be accessed
|
6
|
+
class << self
|
7
|
+
# if a method is called on the class, attempt to look it up in the config array
|
8
|
+
def method_missing(meth, *args, &block)
|
9
|
+
if args.empty? && block.nil?
|
10
|
+
config[meth.to_s]
|
11
|
+
else
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def config
|
19
|
+
@config ||= YAML.load_file(File.join(::Rails.root, 'config', 'amazon_s3.yml'))[::Rails.env]
|
20
|
+
rescue
|
21
|
+
warn('WARNING: s3_cors_fileupload gem was unable to locate a configuration file in config/amazon_s3.yml and may not ' +
|
22
|
+
'be able to function properly. Please run `rails generate s3_cors_upload:install` before proceeding.')
|
23
|
+
{}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module S3CorsFileupload
|
2
|
+
module FormHelper
|
3
|
+
# Options:
|
4
|
+
# :access_key_id The AWS Access Key ID of the owner of the bucket.
|
5
|
+
# Defaults to the `Config.access_key_id` (read from the yaml config file).
|
6
|
+
#
|
7
|
+
# :acl One of S3's Canned Access Control Lists, must be one of:
|
8
|
+
# 'private', 'public-read', 'public-read-write', 'authenticated-read'.
|
9
|
+
# Defaults to `'public-read'`.
|
10
|
+
#
|
11
|
+
# :max_file_size The max file size (in bytes) that you wish to allow to be uploaded.
|
12
|
+
# Defaults to `Config.max_file_size` or, if no value is set on the yaml file `524288000` (500 MB)
|
13
|
+
#
|
14
|
+
# :bucket The name of the bucket on S3 you wish for the files to be uploaded to.
|
15
|
+
# Defaults to `Config.bucket` (read from the yaml config file).
|
16
|
+
#
|
17
|
+
# Any other key creates standard HTML options for the form tag.
|
18
|
+
def s3_cors_fileupload_form(options = {}, &block)
|
19
|
+
policy_helper = PolicyHelper.new(options)
|
20
|
+
# initialize the hidden form fields
|
21
|
+
hidden_form_fields = {
|
22
|
+
:key => '',
|
23
|
+
'Content-Type' => '',
|
24
|
+
'AWSAccessKeyId' => options[:access_key_id] || Config.access_key_id,
|
25
|
+
:acl => policy_helper.options[:acl],
|
26
|
+
:policy => policy_helper.policy_document,
|
27
|
+
:signature => policy_helper.upload_signature,
|
28
|
+
:success_action_status => '201'
|
29
|
+
}
|
30
|
+
# assume that all of the non-documented keys are
|
31
|
+
_html_options = options.reject { |key, val| [:access_key_id, :acl, :max_file_size, :bucket].include?(key) }
|
32
|
+
# return the form html
|
33
|
+
construct_form_html(hidden_form_fields, policy_helper.options[:bucket], _html_options, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def build_form_options(options = {})
|
39
|
+
{ :id => 'fileupload' }.merge(options).merge(:multipart => true, :authenticity_token => false)
|
40
|
+
end
|
41
|
+
|
42
|
+
# hidden fields argument should be a hash of key value pairs (values may be blank if desired)
|
43
|
+
def construct_form_html(hidden_fields, bucket, html_options = {}, &block)
|
44
|
+
# now build the html for the form
|
45
|
+
form_text = form_tag("https://#{bucket}.s3.amazonaws.com", build_form_options(html_options)) do
|
46
|
+
hidden_fields.map do |name, value|
|
47
|
+
hidden_field_tag(name, value)
|
48
|
+
end.join.html_safe + "
|
49
|
+
<!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload -->
|
50
|
+
<div class='row fileupload-buttonbar'>
|
51
|
+
<div class='span7'>
|
52
|
+
<button class='btn btn-success fileinput-button'>
|
53
|
+
<i class='icon-plus icon-white'></i>
|
54
|
+
<span>Add files...</span>
|
55
|
+
".html_safe +
|
56
|
+
file_field_tag(:file, :multiple => true) + "
|
57
|
+
</button>
|
58
|
+
<button class='btn btn-primary start' type='submit'>
|
59
|
+
<i class='icon-upload icon-white'></i>
|
60
|
+
<span>Start upload</span>
|
61
|
+
</button>
|
62
|
+
<button class='btn btn-warning cancel' type='reset'>
|
63
|
+
<i class='icon-ban-circle icon-white'></i>
|
64
|
+
<span>Cancel upload</span>
|
65
|
+
</button>
|
66
|
+
<button class='btn btn-danger delete' type='button'>
|
67
|
+
<i class='icon-trash icon-white'></i>
|
68
|
+
<span>Delete</span>
|
69
|
+
</button>
|
70
|
+
<input class='toggle' type='checkbox'></input>
|
71
|
+
</div>
|
72
|
+
<div class='span5'>
|
73
|
+
<!-- The global progress bar -->
|
74
|
+
<div class='progress progress-success progress-striped active fade'>
|
75
|
+
<div class='bar' style='width: 0%'></div>
|
76
|
+
</div>
|
77
|
+
</div>
|
78
|
+
</div>
|
79
|
+
<!-- The loading indicator is shown during image processing -->
|
80
|
+
<div class='fileupload-loading'></div>
|
81
|
+
<br>
|
82
|
+
<!-- The table listing the files available for upload/download -->
|
83
|
+
<table class='table table-striped' id='upload_files'>
|
84
|
+
<tbody class='files' data-target='#modal-gallery' data-toggle='modal-gallery'></tbody>
|
85
|
+
</table>".html_safe
|
86
|
+
end
|
87
|
+
form_text += capture(&block) if block
|
88
|
+
form_text
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'openssl'
|
3
|
+
require 'digest/sha1'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module S3CorsFileupload
|
7
|
+
class PolicyHelper
|
8
|
+
attr_reader :options
|
9
|
+
|
10
|
+
def initialize(_options = {})
|
11
|
+
# default max_file_size to 500 MB if nothing is received
|
12
|
+
@options = {
|
13
|
+
:acl => 'public-read',
|
14
|
+
:max_file_size => Config.max_file_size || 524288000,
|
15
|
+
:bucket => Config.bucket
|
16
|
+
}.merge(_options).merge(:secret_access_key => Config.secret_access_key)
|
17
|
+
end
|
18
|
+
|
19
|
+
# generate the policy document that amazon is expecting.
|
20
|
+
def policy_document
|
21
|
+
Base64.encode64(
|
22
|
+
{
|
23
|
+
expiration: 1.hour.from_now.utc.strftime('%Y-%m-%dT%H:%M:%S.000Z'),
|
24
|
+
conditions: [
|
25
|
+
{ bucket: options[:bucket] },
|
26
|
+
{ acl: options[:acl] },
|
27
|
+
{ success_action_status: '201' },
|
28
|
+
["content-length-range", 0, options[:max_file_size]],
|
29
|
+
["starts-with", "$utf8", ""],
|
30
|
+
["starts-with", "$key", ""],
|
31
|
+
["starts-with", "$Content-Type", ""]
|
32
|
+
]
|
33
|
+
}.to_json
|
34
|
+
).gsub(/\n|\r/, '')
|
35
|
+
end
|
36
|
+
|
37
|
+
# sign our request by Base64 encoding the policy document.
|
38
|
+
def upload_signature
|
39
|
+
Base64.encode64(
|
40
|
+
OpenSSL::HMAC.digest(
|
41
|
+
OpenSSL::Digest::Digest.new('sha1'),
|
42
|
+
options[:secret_access_key],
|
43
|
+
self.policy_document
|
44
|
+
)
|
45
|
+
).gsub(/\n/, '')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|