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

8
我在我的网站上添加了通过Dropbox Chooser上传文件到我的网站的功能。除了一个问题,一切都正常 - 我不知道如何正确实现“取消”功能。它应该按照以下方式工作 - 当您单击“取消”时 - 相应的文件面板应从UI中删除,并停止从Dropbox上传。
当用户单击“从Dropbox选择”并选择文件并单击“提交”时,我会向Rails发起Ajax请求,调用方法“add_file_via_dropbox”。在这个方法中,我获取要通过“Paperclip” gem上传到“Amazon S3”服务器的Dropbox文件的URL。
当用户单击“取消”时,我想摆脱部分上传到S3的文件,该文件将存储(上传成功后)在“content.attachment”属性中。但是,当他单击“取消”时 - 文件仍在上传(否则他将看不到“取消”链接),我不知道如何立即删除它。
我的另一个想法是,我可以简单地向“内容”添加“已取消”的属性,然后不显示任何“内容”,其中这样的属性==“真”。“Amazon S3”的空间浪费,因此我应该每天运行一次后台任务以删除“已取消”的附件。我希望在第一时间不将它们保存在“Amazon S3”上。有可能吗?我考虑将控制器的操作“add_file_via_dropbox”移动到后台作业中,以便我能够在从客户端接收到“取消”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

后来添加

我更详细地调查了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保存方法:(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)当文件正在下载到我的Rails服务器时,如何取消对open(@target)的调用?
2)当文件从我的Rails服务器上传到S3时,如何取消对s3_object(style).write(file,write_options)的调用?

没有尝试过 RailsAmazon S3done_tmpl 是一个 html 元素吗?还是在 html 元素内的内容?谢谢分享。 - guest271314
tmpl("template-download") 将会扩展为一个 HTML 代码块。 - yaru
请查看帖子。感谢分享。希望这有所帮助。 - guest271314
请参考以下链接:https://dev59.com/K2855IYBdhLWcg3wFABu 和 http://api.jquery.com/jQuery.ajaxTransport/ 希望这能帮到您。 - guest271314
1个回答

3
如果文件在服务器端处理并上传到S3之前仍在上传,那么您需要像已经做过的一样中止连接。即:如何取消/中止jQuery AJAX请求? 如果文件已上传到服务器并正在上传到S3:由于Ruby不是异步基础的,您实际上必须让文件上传,但是由于服务器现在正在上传它,您的客户端可以在技术上离开页面。所以:当您的服务器接收到文件时,请返回一个唯一的ID,如果用户仍然点击取消,则将ID返回并让您的服务器轮询或cron删除文件。

我不想让Rails服务器完全上传文件到S3,因为文件可能非常大(高达1GB),这将浪费我的流量和金钱。如果用户在文件上传过程中点击“取消”,那么很明显用户不再需要该文件,我希望立即取消文件上传。有什么好的想法吗? - yaru
@yaru 一种可能的方法是使用命令+参数生成一个进程。例如,在PHP中,我会执行"php 'file-upload-id'"。然后每隔一分钟在cron中运行某些操作,以查找数据库中的任何删除请求,然后进行进程查找/杀死pid。 - Michael Mikhjian
MKN Web Solutions,请阅读我问题中的“后来添加”部分(刚刚添加)。生成一个进程仍然是解决我的问题的唯一可接受的方法吗? - yaru
@yaru我突然意识到您提到了上传1GB的文件。 您必须记住,用户上传到您的服务器,然后服务器上传到S3。 这不是您设计应用程序的直接上游到S3的方式。因此,在技术上,假设上传需要15-20分钟才能完成1GB,如果用户在此期间取消上传,它将正确地取消对您服务器的上游,此后用户将认为该文件已经上传(而它实际上正在流向S3)。 如果用户想取消上传,他将在这15-20分钟时间范围内完成操作。 我认为您正在过度复杂化情况。 - Michael Mikhjian
MKN Web Solutions,你在之前提到“如果用户在此期间取消操作,它将正确地取消与你的服务器的上行连接”,但这确实是个问题 - 我不知道如何正确地取消与我的服务器的上行连接。上行连接并不是来自客户端本身(我可以通过Ajax的abort()方法来取消它)。相反,我的Rails服务器会根据用户提供的URL下载文件到他的Dropbox远程文件中(请参考我原始问题中的Paperclip中的download_content方法)。我需要在两种情况下进行取消操作:1)从Dropbox下载文件到Rails服务器;2)从Rails服务器上传文件到Amazon S3。 - yaru

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接