簡體   English   中英

使用Rails 3.2和AJAX(非閃存上傳解決方案)將多個文件直接上傳到Amazon S3

[英]Uploading multiple files directly to Amazon S3 using Rails 3.2 and AJAX (non-flash upload solutions)

這個問題困擾了我好幾個小時,我似乎找不到解決方案。

我有一個Rails 3.2應用程序,該應用程序允許用戶使用carrierwave_direct,fog和carrierwave(對carrierwave_direct的依賴性)將文件上傳到Amazon S3帳戶。 通過使用carrierwave_direct,用戶可以通過直接將文件直接發布到Amazon S3來跳過將文件上傳到服務器(對於大型文件,可以節省服務器處理和超時(如Heroku))。

如果您要做的只是選擇1個文件,然后將其上傳到Amazon,並需要redirect_to您提供給Amazon的URL,它會很好地工作。 它通過將表單發布到Amazon S3來執行此操作,然后Amazon響應提供的URL(您在表單中指定此URL),並在URL中添加一些參數,然后將這些參數作為指向文件的指針存儲在模型中的Amazon上。

因此,生命周期為:選擇1個文件,發布到Amazon,Amazon通過URL進行響應,該URL會將您發送到另一個頁面,然后您可以保存帶有指向Amazon文件的指針的記錄。

我一直試圖找出的是如何允許選擇並上傳多個文件並更新上傳進度? 我正在嘗試使用純JavaScript(使用現代瀏覽器提供的文件API)來執行此操作,因此我不需要任何第三者工具。 另外,為了深入學習,我避免使用任何插件,而是嘗試自己編寫代碼。

我想獲得的功能是:

  1. 用戶看到帶有文件字段的表單(或拖放)
  2. 用戶選擇多個文件(單擊文件字段或拖放)
  3. 使用Javascript(尚無服務器),構建要上傳的選定文件的隊列(僅文件名和大小,使用瀏覽器File API)
  4. 用戶然后單擊“開始上傳”按鈕
  5. 遍歷隊列中的每個文件,然后將文件發布到Amazon S3; 亞馬遜將使用URL響應每個單獨的POST,並且該URL需要通過Javascript處理,而不是作為標准請求; Amazon提供的URL將創建一條記錄,該記錄存儲指向Amazon文件的指針; 創建記錄后,代碼將轉到隊列中的下一個文件,直到完成。

在這一點上,我什至可以沒有單獨的進度條。 我很樂意將多個文件發布到Amazon S3,而無需刷新頁面。

我不喜歡任何寶石。 實際上,如果我真的想以特定的方式完成工作,我恐怕將不得不從頭開始編寫我想做的事情。 目標是通過AJAX將多個文件上傳到Amazon S3帳戶。 我將對如何解決該問題的一般概念感到欣喜若狂。 我已經花了很多時間來進行搜索,但還沒有找到能滿足我需要的解決方案。 任何幫助將不勝感激。

編輯2014-03-02

Raj問我如何實施多重上傳。 一直以來,我都不記得我所做的所有“原因”(無論如何這是我第一次來的錯誤代碼),但這就是我一直在做的事情。

我上載的模型是個人鑒定,其中相關圖像存儲在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

根據評論中的建議,使用jQuery Upload: http : //blueimp.github.com/jQuery-File-Upload/

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

可以在以下位置找到“工作”代碼: https : //github.com/joeandrews/s3multipartupload

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM