В Ruby on Rails после метода send_file удалите файл с сервера

Я использую следующий код для отправки файла в Rails.

if File.exist?(file_path)
  send_file(file_path, type: 'text/excel') 
  File.delete(file_path)
end

В этом я пытаюсь отправить файл и удалить файл с сервера после его успешной отправки. Но я столкнулся с проблемой: операция удаления выполняется во время отправки, и из-за этого я ничего не вижу в браузере.

Так есть ли способ в Rails, после завершения операции send_file удалить файл с сервера.

Любая помощь в этом будет высоко оценена.

Спасибо,
Четан


person Chetan Kalore    schedule 14.08.2013    source источник
comment
Я не думаю, что это возможно внутри обработчика запросов, и вам понадобится отложенное задание или аналогичный механизм для очистки, что даст достаточно времени для завершения загрузки.   -  person Neil Slater    schedule 14.08.2013


Ответы (3)


Поскольку вы используете send_file, Rails передаст запрос на ваш HTTP-сервер (nginx, apache и т. д.) — См. документацию Rails по send_file относительно заголовков X-Sendfile). Из-за этого, когда вы пытаетесь удалить файл, 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)

Другим вариантом может быть фоновое задание, которое периодически проверяет наличие временных файлов для удаления.

person Dylan Markow    schedule 14.08.2013
comment
Также имейте в виду, что send_data, вероятно, будет использовать больше ресурсов для отправки того же контента, что и send_file, но не нужно зацикливаться на разнице, если только это не будет часто используемой функцией сайта. - person Neil Slater; 14.08.2013
comment
@NeilSlater Да, для простого текстового файла, который генерируется быстро, это не должно иметь большого значения. Но для создания больших/медленных файлов лучше использовать send_file + фоновое задание. - person Dylan Markow; 14.08.2013
comment
Спасибо, Дилан, за ответ на это. У меня сработало следующее: если File.exist?(file_path) File.open(file_path, 'rb') do |f| send_data f.read, тип: application/excel, имя файла: file_name.xls end end - person Chetan Kalore; 16.08.2013
comment
Спасибо, Дилан, за простое решение (у) - person Taimoor Changaiz; 13.11.2015

Если вы создаете на лету файл, который пытаетесь отправить, решение состоит в том, чтобы использовать 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

Вопреки тому, что указано в этом вопросе, это работает, как и ожидалось (файл остается на сервер достаточно долго обслуживается, но в конечном итоге удаляется); это сообщение указывает, что это связано с обновления в Rails 3.2.11, чего я не мог проверить.

person louije    schedule 19.04.2014
comment
Я почти уверен, что если вы действительно хотите удалить временный файл, вам нужно сделать что-то вроде temp_file.unlink - person Michael Wu; 27.06.2014
comment
@MichaelWu Согласно documentation, TempFile#close без параметров автоматически вызывает #unlink, когда объект завершен. Это сработало, как и ожидалось для меня. - person louije; 04.07.2014
comment
Я бы не стал рассчитывать на то, что это сработает в 100% случаев. Он удалит файл, когда временный файл будет собран мусором, и нет никаких гарантий, что это произойдет до того, как nginx/apache откроет файл. Это зависит от внутренней реализации send_file в Rails. send_data — лучшая альтернатива. - person mrbrdo; 10.03.2015

Это работает для меня! С помощью 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)
person kristina    schedule 03.12.2014