【问题标题】:How to cancel file being uploaded to Amazon S3?如何取消上传到 Amazon S3 的文件?
【发布时间】:2014-05-06 11:53:42
【问题描述】:

我添加了通过 Dropbox 选择器将文件从 Dropbox 上传到我的网站的功能。 一切正常,除了一件事——我不知道如何正确实现Cancel 功能。它应该以这种方式工作 - 当您单击 Cancel 时 - 应该从 UI 中删除相应的文件面板,并且应该停止从 Dropbox 上传。

当用户单击Choose from Dropbox 并选择文件并单击Submit 时,我向Rails 发起Ajax 请求,发送至方法add_file_via_dropbox。在这种方法中,我通过 Paperclip gem 的帮助将 Dropbox 文件上传到 Amazon S3 服务器的 URL

当用户单击Cancel 时,我想删除部分上传到S3 的文件,该文件将存储(成功上传后)在content.attachment 属性中。但是当他点击Cancel - 一个文件仍在上传(否则他不会看到Cancel 链接),我不知道如何立即删除它。

我的另一个想法是,我可以简单地将cancelled 属性添加到content,然后不显示任何content,其中此类属性== true。但是这种方法不会让我免于在Amazon S3 上浪费空间,所以我应该每天运行一次后台任务来删除cancelled 附件。 我不希望首先将它们保存在S3 上。可能吗?我考虑过将控制器的操作add_file_via_dropbox 移动到后台作业,这样我就可以在收到来自客户端的cancel ajax 请求后立即kill 它。我对这一切有点困惑。你将如何解决这个问题?谢谢。

product_form.html.erb

$.ajax({

  // Cancel file uploading on client side via jqXHR.abort()
  // It's quite useless because with 99% probability that request     
  // already was sent to Rails and is processing by appropriate action

  beforeSend: function(jqXHR, options) {
    tmpl.find('a').click(function(e) {
      e.preventDefault();
      jqXHR.abort();
      $(this).parents('li').remove();
    });
  },
  type: "POST",
  dataType: "json",
  url: "<%= add_file_via_dropbox_url %>",
  data: { product_id: $("#fileupload").data("product_id"), file: dropbox_file },
})

// When Rails response is returned - update UI with "successful upload state"
.done(function(json_file) {
  var done_tmpl = $(tmpl("template-download", json_file));
  var li = $(".files_list").find('[data-uuid="' + json_file.uuid + '"]');
  li.replaceWith(done_tmpl);
}
})

product_controller.rb

# This method is called by Ajax request
def add_file_via_dropbox
  product = Product.find(params[:product_id])

  # Each Product has_many Contents(files)
  file = params[:file]
  content = product.contents.build
  content.attachment_remote_url = file[:url]
  content.save

  # Construct json response object for Ajax call
  json_obj = []
  json_obj << {name: content.attachment_file_name,
               size: content.attachment_file_size,
               url: content.attachment.url,
               thumbnailUrl: content.attachment.url,
               deleteUrl: "#{root_url}profile/products/delete_content/#{product.id}/#{content.id}",
               deleteType: "DELETE",
               uuid: file[:uuid]}

  respond_to do |format|
    format.json { render json: json_obj }
  end
end

content.rb

class Content
  attr_reader :attachment_remote_url
  has_attached_file :attachment, bucket: ENV['S3_BUCKET_NAME']

  def attachment_remote_url=(url_value)
    self.attachment = URI.parse(url_value)
  end
end

稍后添加

我更详细地研究了回形针的工作原理:

1) 当这行代码执行时

content.attachment_remote_url = file[:url]

此回形针代码从 paperclip/.../uri_adapter.rb 执行 从给定的 URL(在我的示例中为 Dropbox URL)下载文件并将其保存在本地(在 Rails 服务器的临时文件中)

class UriAdapter < AbstractAdapter
  def initialize(target)
    @target = target
    @content = download_content # <----
    cache_current_values
    @tempfile = copy_to_tempfile(@content)
  end

...

def download_content
  open(@target) # method from 'open-uri' library
end

2) 仅当我在此处保存模型时,此文件才会推送到 S3:

content.attachment_remote_url = file[:url]
content.save # <----

Paperclip save 方法将被调用:(paperclip/.../attachment.rb)

def save
  flush_deletes unless @options[:keep_old_files]
  flush_writes # <-----
  @dirty = false
  true
end

然后flush_writes 将本地文件推送到 S3 (paperclip/.../s3.rb)

def flush_writes    
  # omitted code       
  s3_object(style).write(file, write_options) # method from `aws-sdk` gem
  # omitted code                 
  end

因此,我将最初的问题缩小到这两个:

1) 如何在调用 open(@target) 时取消调用(文件正在下载到我的 Rails 服务器)

2) 当文件从我的 Rails 服务器上传到 S3 时,如何取消对 s3_object(style).write(file, write_options) 的调用?

【问题讨论】:

标签: jquery ruby-on-rails ajax amazon-s3 paperclip


【解决方案1】:

如果在服务器端将文件处理到 S3 之前文件仍在上传到您的服务器,您将需要像以前一样中止连接。 IE。 How to cancel/abort jQuery AJAX request?

如果文件已经上传到服务器并且现在正在上传到 S3:因为 ruby​​ 不是基于异步的,所以你基本上必须让文件上传,但是由于服务器现在正在上传它,你的客户端可以技术上离开页面。所以:当你的服务器收到文件时,我会传回一个唯一的 ID,如果用户仍然点击取消,然后重新输入 ID 并让你的服务器轮询或即 cron 删除文件。

【讨论】:

  • 我不想让 Rails 服务器将文件完全上传到 S3,因为文件可能非常大(高达 1Gb),这会花费我的流量和金钱。如果用户在文件上传时单击“取消”,很明显用户不再需要它,我想立即取消文件上传。有什么想法吗?
  • @yaru 一种可能的方法是使用命令 + arg 生成一个进程,即在 PHP 中,我会执行“php 'file-upload-id'”。然后每隔一分钟用 cron 运行一次以查找数据库中的任何删除请求,然后执行一个进程查找/终止 pid。
  • MKN Web 解决方案,请阅读我的问题中的“稍后添加”部分(刚刚添加)。生成一个进程是否仍然是我的问题的唯一可接受的解决方案?
  • @yaru 我刚刚意识到您提到上传 1GB 文件,您必须记住用户上传到您的服务器,然后服务器上传到 S3。它不是 S3 的直接上游,就像您设计应用程序的方式一样。所以技术上假设上传 1GB 需要 15-20 分钟,如果用户在此期间取消将正确取消上游到您的服务器,此后用户会认为文件已经上传(当它正在流式传输到 S3 时)。如果用户想取消上传,他会在 15-20 分钟的时间内完成。我认为你把情况复杂化了。
  • MKN Web Solutions,您写道“如果用户在此期间取消将正确取消您服务器的上游”但这是一个问题 - 我不知道如何正确取消上游到我的服务器.上游不是来自客户端本身(我会通过 Ajax abort() 方法取消它)。相反,我的 Rails 服务器本身将用户提供的 URL 指定的文件下载到他的 Dropbox 远程文件。 (请参阅我原来的问题中 Paperclip 的方法 download_content )。我需要在 2 种情况下取​​消:1)文件从 Dropbox 下载到 Rails 服务器; 2) 从 Rails 服务器上传文件到 Amazon S3。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-02
  • 2011-04-07
  • 1970-01-01
  • 2019-12-10
相关资源
最近更新 更多