【问题标题】:In Ruby on Rails, After send_file method delete the file from server在 Ruby on Rails 中,在 send_file 方法之后从服务器中删除文件
【发布时间】:2014-12-04 00:18:19
【问题描述】:

我正在使用以下代码在 Rails 中发送文件。

如果 File.exist?(file_path) 发送文件(文件路径,类型:'文本/excel') File.delete(file_path) 结尾

在此我尝试发送文件并在成功发送后从服务器中删除文件。但我面临的问题是,删除操作在发送时正在执行,因此我在浏览器中看不到任何内容。

那么在 Rails 中有什么办法,一旦 send_file 操作完成,就从服务器中删除文件。

对此的任何帮助将不胜感激。

谢谢,
车坦

【问题讨论】:

  • 我不认为这在请求处理程序中是可能的,你需要一个延迟的工作或类似的机制来清理,以便有足够的时间来完成下载

标签: ruby-on-rails ruby file x-sendfile


【解决方案1】:

因为您使用的是send_file,Rails 会将请求传递给您的 HTTP 服务器(nginx、apache 等 - See the Rails documentation on send_file regarding X-Sendfile headers)。因此,当您尝试删除该文件时,Rails 并不知道它仍在使用中。

您可以尝试改用send_data,它会一直阻塞直到数据发送完毕,让您的File.delete 请求成功。请记住,send_data 需要数据流作为其参数,而不是路径,因此您需要先打开文件:

File.open(file_path, 'r') do |f|
  send_data f.read, type: "text/excel"
end
File.delete(file_path)

另一个选项是定期检查要删除的临时文件的后台作业。

【讨论】:

  • 另外请记住,send_data 可能会使用更多资源来发送与send_file 相同的内容,但无需担心差异,除非这将是该网站的一个频繁使用的功能。
  • @NeilSlater 是的,对于快速生成的基本文本文件来说,它应该没什么大不了的。但是对于生成大/慢文件,send_file + 后台作业解决方案可能会更好。
  • 感谢 Dylan 对此作出回应。以下对我有用: if File.exist?(file_path) File.open(file_path, 'rb') do |f| send_data f.read,类型:“application/excel”,文件名:“file_name.xls” end end
  • 感谢 Dylan 提供简单的解决方案(y)
【解决方案2】:

如果您正在动态生成您尝试发送的文件,一个解决方案是使用Tempfile 类:

begin
  # The second argument is optional:
  temp_file = Tempfile.new(filename, temp_directory)

  # ... edit temp_file as needed.

  # By default, temp files are 0600,
  # so this line might be needed depending on your configuration:
  temp_file.chmod(0644)
  send_file temp_file
ensure
  temp_file.close
end

this question 中的指示相反,这按预期工作(文件在服务器上停留的时间足够长,可以提供服务,但最终会被删除); this post 似乎表明这是由于 Rails 3.2.11 中的更新,我无法验证。

【讨论】:

  • 我很确定如果你想真正删除临时文件,你需要做一些类似 temp_file.unlink
  • @MichaelWu 根据documentation,没有参数的 TempFile#close 最终会自动调用 #unlink,“当对象完成时”。它对我来说按预期工作。
  • 我不会指望这种工作 100% 的时间。当 Tempfile 被垃圾回收时,它会删除文件,并且不能保证在 nginx/apache 打开文件之前会发生这种情况。这取决于 Rails 中 send_file 的内部实现。 send_data 是更好的选择。
【解决方案3】:

这对我有用! 使用send_data,您可以在发送前删除文件。

file = File.open(Rails.root.join('public', 'uploads', filename), "rb")
contents = file.read
file.close

File.delete(filepath) if File.exist?(filepath)

send_data(contents, :filename => filename)

【讨论】:

    猜你喜欢
    • 2012-11-01
    • 2014-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-05
    • 2023-04-08
    • 1970-01-01
    相关资源
    最近更新 更多