rails - x-sendfile + temporary files
Asked Answered
C

4

8

Some time ago I wrote a question regarding the use of temporary files within a rails app. On thar particular case, I decided to use tempfile

This causes a problem if I also want to use the x-sendfile directive (as a parameter in Rails 2, or as a configuration option in Rails 3) so that the file sending is handled by my web server directly, not my rails app.

So I thought about doing something like this:

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

This setup has one issue: the file is deleted before being sent!

On one hand, tempfile will delete the file as soon as the Tempfile.open block is over. On the other, x-sendfile makes the send_file call asynchronous - it returns very quickly, so the server hardly has time to send the file.

My best possible solution right now involves using non-temporary files (File instead of Tempfile), and then a cron task that erases the temp folder periodically. This is a bit inelegant since:

  • I have to use my own tempfile naming scheme
  • Files stay on the tmp folder longer than they are needed.

Is there a better setup? Or, is there at least a "success" callback on the asynchronous send_file, so I can erase f when it's done?

Thanks a lot.

Coaming answered 18/5, 2011 at 10:35 Comment(0)
C
2

Given that Rails3 uses x-sendfile when it is available, and there is no way to deactivate it, you just can't use send_file with a library such as TempFile. The best option is the one I mentioned in the question: use a regular File, and have a cron task that removes old temp files periodically.

EDIT: The removal of unused files has now been easier to deal with with the maid gem:

https://github.com/benjaminoakes/maid

Coaming answered 13/7, 2012 at 21:3 Comment(2)
Thanks for the mention of Maid. I appreciate it. :)Bluefish
Thank you for creating it! :)Coaming
T
0

don't put send_file in block.

f = Tempfile.new('prefix', "#{Rails.root}/tmp")
f.print('a temp message')
f.close
send_file(f.path, :x-sendfile => true)

then using another script to cleanup tempfile

Tempietempla answered 18/5, 2011 at 12:4 Comment(5)
I'm afraid that will not work anyway. Tempfile is "smart enough" to detect that the scope in which f is defined has ended, and removes the file anyway. The block just makes it a bit more explicit.Coaming
don't guess, just try it: 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)Tempietempla
I tried. f's scope is still active on your example. Exit the console and the file will be gone. Same happens when a controller action returns.Coaming
f's scope has ended, but gc unlink the file. see Tempfile#callback. Using a normal file will work.Tempietempla
I know. That's what I'm doing now. I'm asking if there's a better way.Coaming
V
0

How about the file-temp gem? https://github.com/djberg96/file-temp

require 'file/temp'

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

Like the zzzhc's answer, you would need to manage cleanup externally

Viaticum answered 24/5, 2011 at 18:46 Comment(1)
Thanks for your answer. If I have to manage the files myself, then I think I would stick with regular File instances. The other thing that library provides (calculating the default temp file folder depending on the host) isn't interesting for me, since I already have "#{Rails.root}/tmp"Coaming
F
0

You can undefine Tempfile instance's finalizer so that your file never gets deleted when the instance is detroyed and then let the chron task handle it.

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
Forcier answered 27/9, 2013 at 8:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.