2012-07-22 104 views
5

這個問題一直困擾着我很多小時,我似乎無法找到解決方案。使用Rails 3.2和AJAX(非Flash上​​傳解決方案)將多個文件直接上傳到Amazon S3

我有一個rails 3.2應用程序,允許用戶使用carrierwave_direct,fog和carrierwave(依賴於carrierwave_direct)將文件上傳到Amazon S3帳戶。使用carrierwave_direct允許用戶通過直接將文件上傳到服務器上來跳過上傳文件到服務器(保存服務器處理和像Heroku這樣的大文件超時)。

如果您所做的只是選擇1個文件,將其上傳到Amazon,並希望將您的Amazon提供的URL重定向到某個URL,那麼它工作正常。它通過將表單發佈到Amazon S3來執行此操作,而Amazon則使用URL中的一些參數來響應提供的URL(您在表單中指定此URL),然後將其存儲爲模型中Amazon文件的指針。

所以生命週期是:選擇1個文件,POST到亞馬遜,Amazon響應一個URL,將URL發送到另一個頁面,然後您可以使用指向Amazon文件的指針保存記錄。

我一直在想的是如何讓多個文件被選擇和上傳並更新上傳進度?我試圖用純JavaScript(使用現代瀏覽器提供的文件API)來做到這一點,所以我不想要任何第三方工具。此外,爲了深入學習,我避開了任何插件,並試圖自己編寫代碼。

我想獲得的功能是:

  1. 用戶看到表格文件中的字段(或拖/放)
  2. 用戶選擇多個文件(或者單擊文件字段或拖/放)
  3. 使用JavaScript(無服務器還),構建選定的文件隊列上傳(只是文件名和大小,使用瀏覽器文件API),那麼
  4. 用戶點擊「開始上傳」按鈕
  5. 遍歷在隊列中的每個文件一個d將文件發佈到Amazon S3;亞馬遜將使用URL對每個POST進行響應,並且該URL需要通過Javascript進行處理,而不是作爲標準請求; Amazon提供的URL將創建一條記錄,存儲指向亞馬遜文件的指針;一旦創建了記錄,代碼就會進入隊列中的下一個文件直到完成。

在這一點上,我甚至可以沒有單獨的進度條;我很高興能夠在沒有頁面刷新的情況下將多個文件發佈到Amazon S3。

我不偏向任何寶石。我實際上害怕如果我真的想以特定的方式完成,我將不得不從頭開始寫我想做的事。目標是通過AJAX將多個文件上傳到Amazon S3帳戶。即使是關於如何解決問題的一般概念,我也會欣喜若狂。我花了很多小時搜索這個,我只是沒有找到任何我想做的解決方案。任何幫助都將不勝感激。

編輯2014年3月2日

拉吉問我是如何實現我的多個上傳。已經很長時間了,我不記得我做過的所有「爲什麼」(反正可能是壞代碼,因爲這是我的第一次),但這是我所做的。

我上傳的模型是Testimonial,其中有一個關聯的圖像存儲在Amazon S3中。它允許用戶選擇多個圖像(我認爲它們實際上是轉換爲圖像的PDF文件)並將它們拖放到屏幕上。在上傳時,我展示了一種模式,向用戶反饋了需要多長時間。

我不會假裝記住我在很多方面做了什麼,但是如果它有助於隨意使用它。

# Gemfile 
# For client-side multiple uploads 
gem "jquery-fileupload-rails" 

# For file uploads and Amazon S3 storage 
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 # seconds to convert/process each uploaded file 

    $("#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 # 5 MB 
    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 
+2

使用此:http://blueimp.github.com/jQuery-File-Upload/ – apneadiving 2012-07-22 22:27:36

+0

@apneadiving工作正是我想要的。插件頁面提供了一些非常有用的示例,這些示例向我指出了正確的方向,並且我已經實現了我想要的內容。如果您對問題做出回答而不是評論,我會很樂意接受您的回答。非常感謝你,你幫了我很多努力。 – 2012-07-24 17:33:17

+0

很高興閱讀:) – apneadiving 2012-07-24 19:09:57

回答

6

正如評論建議,使用jQuery上傳:http://blueimp.github.com/jQuery-File-Upload/

+7

雖然這可能在理論上回答這個問題,[這將是更可取的](http://meta.stackexchange.com/q/8259)在這裏包括答案的基本部分,並提供參考鏈接。 – 2014-03-02 18:54:13

+1

@IlmariKaronen我發佈了請求。但是,如果你喜歡 – apneadiving 2014-03-02 19:17:59

+0

,隨時可以downvote有人標記你的答案爲「不是答案」。我不完全同意,但它*是非常有邊界的。也就是說,這個問題也不是最清晰的問題。 – 2014-03-02 19:21:30

0

我已經開始爲此功能編寫基本庫。我在github上有一個工作版本,並且正在撰寫一系列博客文章來詳細說明如何實現這一點。

'工作'代碼可以在:https://github.com/joeandrews/s3multipartupload找到。

相關問題