【问题标题】:Uploading multiple files directly to Amazon S3 using Rails 3.2 and AJAX (non-flash upload solutions)使用 Rails 3.2 和 AJAX 将多个文件直接上传到 Amazon S3(非闪存上传解决方案)
【发布时间】:2012-07-20 19:38:26
【问题描述】:

这个问题困扰了我好几个小时,我似乎找不到解决办法。

我有一个 rails 3.2 应用程序,它允许用户使用 carrierwave_direct、fog 和 carrierwave(carrierwave_direct 的依赖项)将文件上传到 Amazon S3 帐户。使用carrierwave_direct 允许用户通过将文件直接POST 到Amazon S3 来跳过将文件上传到服务器(节省服务器处理和像Heroku 处理大文件那样的超时)。

如果您所做的只是选择 1 个文件,将其上传到亚马逊,并想要一个重定向到您提供给亚马逊的 URL,那么它工作正常。它通过将表单发布到 Amazon S3 来实现此目的,Amazon 使用 URL 中的一些参数响应提供的 URL(您在表单中指定此 URL),然后将这些参数存储为指向模型中 Amazon 上文件的指针。

所以生命周期是:选择 1 个文件,POST 到 Amazon,Amazon 会用一个 URL 响应,将您发送到另一个页面,然后您可以使用指向 Amazon 文件的指针保存一条记录。

我一直想弄清楚的是如何允许选择和上传多个文件并更新上传进度?我正在尝试使用纯 javascript(使用现代浏览器提供的文件 API)来做到这一点,所以我不想要任何 3rd 方工具。此外,为了深入了解这一点,我避免使用任何插件并尝试自己编写代码。

我想要获得的功能是:

  1. 用户看到带有文件字段(或拖放)的表单
  2. 用户选择多个文件(单击文件字段或拖放)
  3. 使用 Javascript(尚无服务器),构建一个选择文件的队列以上传(仅文件名和大小,使用浏览器 File API)
  4. 然后用户点击“开始上传”按钮
  5. 遍历队列中的每个文件并将文件 POST 到 Amazon S3;亚马逊将使用 URL 响应每个单独的 POST,并且该 URL 需要通过 Javascript 处理,而不是作为标准请求;亚马逊提供的 URL 将创建一条记录,存储指向亚马逊文件的指针;创建记录后,代码将转到队列中的下一个文件,直到完成。

此时,我什至可以不用单独的进度条;我很乐意在不刷新页面的情况下将多个文件发布到 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

【问题讨论】:

  • @apneadiving 完全符合我的要求。插件页面有一些非常有用的示例,这些示例为我指明了正确的方向,并且我已经实现了我想要的。如果您回答问题而不是发表评论,我将很乐意接受您的回答。非常感谢,你帮我省了很多力气。
  • 您能分享一下您是如何处理多个文件和 S3 的吗?我可以上传单个文件,但不知道如何一次上传多个文件。
  • 如果您想编写代码以使所有不同的 S3 API 调用客户端、生成策略、处理特定的 S3 错误等,请自己使用 jquery 文件上传。如果您想要一些只适用于简单到极其复杂的上传到 S3 工作流的东西,请使用 Fine Uploader

标签: jquery ruby-on-rails ruby-on-rails-3 file-upload amazon-s3


【解决方案1】:

按照评论中的建议,使用 jQuery 上传:http://blueimp.github.com/jQuery-File-Upload/

【讨论】:

  • 虽然理论上这可以回答这个问题,it would be preferable 在这里包含答案的基本部分,并提供链接以供参考。
  • @IlmariKaronen 我根据提问者的要求发布了。但是,如果您愿意,请随意投反对票
  • 有人将您的答案标记为“不是答案”。我不太同意,但它非常接近。也就是说,这个问题也不是最清楚的。
  • 这是一个工作链接:github.com/blueimp/jQuery-File-Upload
【解决方案2】:

我已经开始为此功能编写一个基本库。我在 github 上有一个工作版本,正在写一系列博客文章来详细说明如何实现这一点。

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

【讨论】:

    猜你喜欢
    • 2013-08-27
    • 2011-09-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-23
    • 2013-10-25
    • 1970-01-01
    • 1970-01-01
    • 2018-10-09
    相关资源
    最近更新 更多