rails - x-sendfile + временные файлы

Некоторое время назад я написал вопрос относительно использование временных файлов в приложении rails. В конкретном случае я решил использовать tempfile.

Это вызывает проблему, если я также хочу использовать директиву x-sendfile (как параметр в Rails 2 или как параметр конфигурации в Rails 3), чтобы отправка файла обрабатывалась непосредственно моим веб-сервером, а не моим приложением 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 асинхронным — он возвращается очень быстро, поэтому сервер вряд ли успевает отправить файл.

Мое лучшее возможное решение на данный момент включает использование невременных файлов (файл вместо временного файла), а затем задачу cron, которая периодически стирает временную папку. Это немного неэлегантно, так как:

  • Я должен использовать свою собственную схему именования временных файлов.
  • Файлы остаются в папке tmp дольше, чем это необходимо.

Есть ли лучшая установка? Или есть ли хотя бы обратный вызов «успех» для асинхронного send_file, чтобы я мог стереть f, когда это будет сделано?

Большое спасибо.


person kikito    schedule 18.05.2011    source источник


Ответы (4)


Учитывая, что Rails3 использует x-sendfile, когда он доступен, и его нельзя деактивировать, вы просто не можете использовать send_file с такой библиотекой, как TempFile. Лучший вариант — тот, который я упомянул в вопросе: используйте обычный файл и установите задачу cron, которая периодически удаляет старые временные файлы.

РЕДАКТИРОВАТЬ: удаление неиспользуемых файлов теперь стало проще с драгоценным камнем горничной:

https://github.com/benjaminoakes/maid

person kikito    schedule 13.07.2012
comment
Спасибо за упоминание горничной. Я ценю его. :) - person Benjamin Oakes; 20.12.2012
comment
Спасибо за его создание! :) - person kikito; 20.12.2012

не помещайте send_file в блок.

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

затем с помощью другого скрипта очистить временный файл

person zzzhc    schedule 18.05.2011
comment
Боюсь, что все равно не получится. Tempfile достаточно умен, чтобы обнаружить, что область действия, в которой определено f, закончилась, и все равно удалит файл. Блок просто делает это немного более явным. - person kikito; 18.05.2011
comment
не гадайте, просто попробуйте: 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(путь) - person zzzhc; 18.05.2011
comment
Я попытался. область f все еще активна в вашем примере. Выйдите из консоли, и файл исчезнет. То же самое происходит, когда действие контроллера возвращается. - person kikito; 18.05.2011
comment
f область действия закончилась, но gc отменяет связь с файлом. см. обратный вызов Tempfile#. Использование обычного файла будет работать. - person zzzhc; 18.05.2011
comment
Я знаю. Это то, что я делаю сейчас. Я спрашиваю, есть ли лучший способ. - person kikito; 18.05.2011

Как насчет файла временных файлов? 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, вам нужно будет управлять очисткой извне.

person SoobAeroDude    schedule 24.05.2011
comment
Спасибо за Ваш ответ. Если мне придется управлять файлами самостоятельно, я думаю, что буду использовать обычные экземпляры файлов. Другая вещь, которую предоставляет библиотека (вычисление папки с временными файлами по умолчанию в зависимости от хоста), меня не интересует, поскольку у меня уже есть #{Rails.root}/tmp - person kikito; 25.05.2011

Вы можете отменить определение финализатора экземпляра 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
person alediaferia    schedule 27.09.2013