Rails - x-sendfile + 临时文件

8
不久前,我写了一个关于在rails应用程序中使用临时文件的问题的问题。在那个特定的情况下,我决定使用tempfile
如果我还想使用x-sendfile指令(作为Rails 2中的参数或Rails 3中的配置选项),以便文件发送由我的Web服务器直接处理,而不是我的Rails应用程序,则会出现问题。
因此,我考虑做这样的事情:
require 'tempfile'

def foo()
  # creates a temporary file in tmp/
  Tempfile.open('prefix', "#{Rails.root}/tmp") do |f|
    f.print('a temp message')
    f.flush
    send_file(f.path, :x_sendfile => true) # send_file f.path in rails 3
  end
end

这个设置有一个问题:文件在发送之前被删除了!
一方面,`tempfile` 会在 `Tempfile.open` 块结束时立即删除文件。另一方面,`x-sendfile` 使 `send_file` 调用异步 - 它非常快地返回,因此服务器几乎没有时间发送文件。
我目前最好的解决方案涉及使用非临时文件(`File` 而不是 `Tempfile`),然后使用 cron 任务定期清除临时文件夹。这有点不优雅,因为:
- 我必须使用自己的临时文件命名方案 - 文件在 tmp 文件夹中停留的时间比需要的时间长。
是否有更好的设置?或者,至少有一个异步的 `send_file` “成功”回调,这样我就可以在完成时删除 f 吗?
非常感谢。
4个回答

2
鉴于Rails3在可用时使用x-sendfile,且没有办法停用它,因此您不能使用TempFile等库来发送文件。最好的选择是我在问题中提到的:使用常规文件,并有一个cron任务定期删除旧的临时文件。
编辑:现在使用maid gem更容易处理未使用的文件的删除:

https://github.com/benjaminoakes/maid


感谢提到Maid。我很感激。 :) - Benjamin Oakes
1
谢谢您的创作! :) - kikito

0
不要把 send_file 放在块中。
f = Tempfile.new('prefix', "#{Rails.root}/tmp")
f.print('a temp message')
f.close
send_file(f.path, :x-sendfile => true)

然后使用另一个脚本清理临时文件


我担心那样做不会起作用。Tempfile足够“聪明”,可以检测到f定义的范围已经结束,并且无论如何都会删除该文件。这个块只是让它更加明确一些。 - kikito
def create_tmp_file f = Tempfile.new("foo", ".") f.write("a" * 1024) f.close f.path end path = create_tmp_file sleep 60 puts File.exists?(path) puts File.read(path) - zzzhc
我尝试过了。在你的示例中,f的范围仍然是活动的。退出控制台,文件将消失。当控制器操作返回时也会发生同样的情况。 - kikito
f的作用域已经结束,但是gc仍然会取消文件链接。请参见Tempfile#callback。使用普通文件将起作用。 - zzzhc
我知道。那就是我现在正在做的。我在问是否有更好的方法。 - kikito

0

文件临时宝石怎么样? https://github.com/djberg96/file-temp

require 'file/temp'

fh = File::Temp.new(false)
fh.puts "world"
fh.close # => Tempfile still on your filesystem

和zzzhc的答案一样,你需要在外部管理清理工作


感谢您的回答。如果我必须自己管理文件,那么我认为我会坚持使用常规的文件实例。该库提供的另一件事情(根据主机计算默认临时文件夹)对我来说并不重要,因为我已经有了"#{Rails.root}/tmp"。 - kikito

0

您可以取消定义Tempfile实例的终结器,这样当实例被销毁时,文件就不会被删除,然后让计划任务来处理它。

require 'tempfile'

def foo()
  # creates a temporary file in tmp/
  Tempfile.open('prefix', "#{Rails.root}/tmp") do |f|
    f.print('a temp message')
    f.flush
    ObjectSpace.undefine_finalizer(f) # 'disables' deletion when GC'ed
    send_file(f.path, :x_sendfile => true) # send_file f.path in rails 3
 end
end

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