Загрузка нескольких файлов напрямую в Amazon S3 с использованием Rails 3.2 и AJAX (не -решения для быстрой загрузки)

Эта проблема беспокоила меня в течение многих часов, и я не могу найти для нее решения.

У меня есть приложение rails 3.2, которое позволяет пользователям загружать файлы в учетную запись Amazon S3 с использованием несущей волны _direct, тумана и зависимости несущей волны (для несущей волны _direct ). Использование Carrierwave _direct позволяет пользователю пропустить загрузку файла на сервер, отправив его напрямую в Amazon S3 (. Это экономит серверную обработку и тайм-ауты, как Heroku для больших файлов ).

Он отлично работает, если все, что вы делаете, это выбираете 1 файл, загружаете его на Amazon и хотите перенаправить _на URL-адрес, который вы предоставляете Amazon. Он делает это, отправляя форму в Amazon S3, и Amazon отвечает на предоставленный URL-адрес (, вы указываете этот URL-адрес в своей форме )с некоторыми параметрами в URL-адресе, которые затем сохраняются как указатель на файл на Amazon. в вашей модели.

Таким образом, жизненный цикл таков: :выберите 1 файл, отправьте POST в Amazon, Amazon ответит URL-адресом, который отправляет вас на другую страницу, и затем вы можете сохранить запись с указателем на файл Amazon.

Я пытался выяснить, как разрешить выбор и загрузку нескольких файлов и обновить ход загрузки? Я пытаюсь сделать это с помощью чистого javascript (, используя файловый API, предоставляемый современными браузерами ), поэтому мне не нужны сторонние инструменты. Кроме того, стремясь изучить это -глубже, я избегаю любых плагинов и пытаюсь писать код самостоятельно.

Функциональность, которую я пытаюсь получить, это:

  1. Пользователь видит форму с полем файла (или перетаскиванием)
  2. Пользователь выбирает несколько файлов (либо щелкает поле файла, либо перетаскивает мышью)
  3. Используя Javascript (пока нет серверов ), создайте очередь из выбранных файлов для загрузки (только имя файла и размер, используя File API браузера)
  4. Затем пользователь нажимает кнопку «Начать загрузку»
  5. . Перебрать каждый файл в очереди и отправить файл в Amazon S3; Amazon будет отвечать на каждый отдельный POST URL-адресом, и этот URL-адрес необходимо обрабатывать с помощью Javascript, а не как стандартный запрос; URL-адрес, предоставленный Amazon, создаст запись, в которой будет храниться указатель на файл Amazon; после создания записи код переходит к следующему файлу в очереди до завершения.

На данный момент я мог бы обойтись даже без отдельного индикатора выполнения; Я был бы счастлив просто отправить несколько файлов в Amazon S3 без обновления страницы.

Я не пристрастен ни к одному из драгоценных камней. На самом деле я боюсь, что мне придется писать то, что я хочу сделать, с нуля, если я действительно хочу, чтобы это было сделано определенным образом. Цель — загрузка нескольких файлов в учетную запись Amazon S3 через AJAX. Я был бы в восторге даже от общих представлений о том, как подойти к проблеме. Я провел много часов в поиске этого, и я просто не нашел решений, которые делают то, что я хочу. Любая помощь вообще будет принята с благодарностью.

РЕДАКТИРОВАТЬ 2014 -03 -02

Радж спросил, как я реализовал множественную загрузку. Это было так давно, что я уже не помню всех «почему» того, что я сделал (, вероятно, все равно плохой код, так как это был мой первый раз ), но вот что у меня было.

Модель, которую я загружал, была 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
5
задан Dan L 2 March 2014 в 14:16
поделиться