kikito
kikito

Reputation: 52648

rails - x-sendfile + temporary files

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:

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.

Upvotes: 8

Views: 5441

Answers (4)

alediaferia
alediaferia

Reputation: 2617

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

Upvotes: 0

kikito
kikito

Reputation: 52648

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

Upvotes: 2

SoobAeroDude
SoobAeroDude

Reputation: 69

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

Upvotes: 0

zzzhc
zzzhc

Reputation: 395

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

Upvotes: 0

Related Questions