This problem bothered me for many hours, and I can not find a solution for it.
I have a rails 3.2 app that allows users to upload files to an Amazon S3 account using carrierwave_direct, fog and carrierwave (dependency for carrierwave_direct). Using carrierwave_direct allows the user to skip uploading the file to the server by sending it directly to Amazon S3 (saves server processing and timeouts such as Heroku for large files).
It works great if all you do is select 1 file, upload it to Amazon and get the redirect_to URL that you provide to Amazon. It does this by POSTing the form for Amazon S3, and Amazon responds to the provided URL (you specify this URL in your form) with some parameters in the URL, which are then saved as a pointer to a file on Amazon in your model.
So, the life cycle: select 1 file, POST for Amazon, Amazon will respond with a URL that will send you to another page, and you can save the record with a pointer to the Amazon file.
What I'm trying to understand is how to allow the selection and loading of multiple files and update the download progress? I am trying to do this with pure javascript (using the file API provided by modern browsers), so I don't need third-party tools. In addition, in pursuit of in-depth study, I avoid any plugins and try to write code myself.
The function I'm trying to get is:
- The user sees a form with a file field (or drag and drop)
- User selects multiple files (either click file field or drag and drop)
- Using Javascript (no servers yet), create a queue of selected files for download (just the file name and size using the browser file API)
- Then the user clicks the “Start Download” button.
- POST Amazon S3; Amazon POST URL- URL- Javascript, ; URL, Amazon, , Amazon; .
; POSTed Amazon S3 .
- . , , , , . Amazon S3 AJAX. , . , , , . .
EDIT 2014-03-02
, . , "" , (, , ), .
, , , , Amazon S3. ( , PDF, ) . , , .
, , , , .
gem "jquery-fileupload-rails"
gem "rmagick"
gem "carrierwave"
gem "fog"
:
# app/views/testimonials/new.html.erb
<div id="main" class="padded">
<div class="center">
<div id="dropzone">
Click or Drop Files here to Upload
</div>
<%= form_for @testimonial do |f| %>
<div class="field">
<%= file_field_tag :image, multiple: true, name: "testimonial[image]", id: "testimonial_image" %>
</div>
<% end %>
</div>
</div>
<div id="mask"></div>
<div id="modal">
<h1>
Uploading <span id="global-upload-count">0</span> Files...
</h1>
<div id="global-progress">
<div id="global-progress-bar" style="width: 0%">
<div id="global-progress-percentage">0%</div>
</div>
</div>
<div id="global-processing">
<span class="spinner"></span> Processing...<span id="global-processing-count">0</span> sec
</div>
</div>
<script id="template-upload" type="text/x-tmpl">
<div class="upload">
{%=o.name%} ({%=o.readable_size%})
<div class="float-right percentage"></div>
<div class="progress"><div class="bar" style="width: 0%"></div></div>
</div>
</script>
JS:
number_to_human_size = (bytes) ->
sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']
i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)))
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i]
dropzone_hover = (e) ->
e.preventDefault()
$(this).addClass("dropzone-hover")
dropzone_leave = (e) ->
e.preventDefault()
$(this).removeClass("dropzone-hover")
jQuery ->
global_count = 0
seconds_to_process = 0
processing_factor = 5
$("#testimonial_image").hide()
dropzone = $("#dropzone")
dropzone.bind "click", (e) ->
$("#testimonial_image").click()
dropzone.bind("dragover", dropzone_hover)
dropzone.bind("dragleave", dropzone_leave)
dropzone.bind("drop", dropzone_leave)
$("#new_testimonial").data("global-count", "0")
$("#new_testimonial").fileupload
dropZone: $("#dropzone")
maxFileSize: 5000000
dataType: "script"
add: (e, data) ->
file = data.files[0]
file.readable_size = number_to_human_size(file.size)
data.context = $(tmpl("template-upload", file).trim())
$("#new_testimonial").append(data.context)
data.submit()
global_count += 1
progress: (e, data) ->
if data.context
progress = parseInt(data.loaded / data.total * 100, 10)
data.context.find(".bar").css("width", progress + "%")
data.context.find(".percentage").text(progress + "%")
submit: (e, data) ->
$("#mask").show()
$("#modal").center().show()
progressall: (e, data) ->
$("#global-upload-count").text(global_count)
global_progress = parseInt(data.loaded / data.total * 100, 10)
$("#global-progress-bar").css("width", global_progress + "%")
$("#global-progress-percentage").text(global_progress + "%")
if global_progress >= 100
seconds_to_process = global_count * processing_factor
$("#global-processing-count").text(seconds_to_process)
$("#global-processing").show()
timer = setInterval(->
seconds_to_process = seconds_to_process - 1
$("#global-processing-count").text(seconds_to_process)
if seconds_to_process == 0
clearInterval(timer)
global_count = 0
seconds_to_process = 0
$("#modal, #mask").hide(0)
, 1000)
:
class Testimonial < ActiveRecord::Base
mount_uploader :image, ImageUploader
def display_name
if name.blank?
return "Testimonial #{self.id}"
else
return name
end
end
end