如何取消上传到Amazon S3的文件?

时间:2014-03-27 13:14:01

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

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

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

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

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

http://i61.tinypic.com/am9p8n.png

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

稍后添加

我详细研究了Paperclip的工作原理:

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服务器)时,如何取消对s3_object(style).write(file, write_options)的呼叫

2)当文件从我的Rails服务器上传到S3时,如何取消对{{1}}的调用?

1 个答案:

答案 0 :(得分:2)

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

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