Использование Send_File для удаленного источника (Ruby on Rails)
В моем приложении у меня есть требование, которое меня превзошло.
У меня есть файл, хранящийся на S3, и когда пользователь нажимает на ссылку в моем приложении, я вхожу в базу данных, которую они кликнули по ссылке, уменьшив уровень "загружаемого кредита" на один, а затем я хочу пригласить файл для загрузки.
Я не просто хочу перенаправить пользователя в файл, потому что он хранится в S3, и я не хочу, чтобы у них была ссылка исходного файла (чтобы я мог поддерживать целостность и доступ)
Похоже, send_file() не работает с удаленным исходным файлом, кто-нибудь порекомендует драгоценный камень или подходящий код, который это сделает?
Ответы
Ответ 1
Вам нужно будет передать содержимое файла пользователю, читая его из ведра/объекта S3.
Если вы используете библиотеку AWS:: S3, возможно, что-то вроде этого:
send_file_headers!( :length=>S3Object.about(<s3 object>, <s3 bucket>)["content-length"], :filename=><the filename> )
render :status => 200, :text => Proc.new { |response, output|
S3Object.stream(<s3 object>, <s3 bucket>) do |chunk|
output.write chunk
end
}
Этот код в основном копируется из кода send_file, который сам по себе работает только для локальных файлов или файловых объектов
N.B. Я бы не советовал подавать файл из самого процесса рельсов. Если возможно/приемлемо для вашего случая использования, я бы использовал аутентифицированный GET для обслуживания личных данных из ведра.
Используя аутентифицированный GET, вы можете сохранить ведро и его объекты частными, одновременно предоставляя временное разрешение для чтения определенного содержимого объекта, создавая URL-адрес, который включает токен подписи аутентификации. Пользователь просто перенаправляется на аутентифицированный URL-адрес, а токен может быть действителен всего несколько минут.
Используя вышеупомянутый AWS:: S3, вы можете получить аутентифицированный URL-адрес GET таким образом:
time_of_exipry = Time.now + 2.minutes
S3Object.url_for(<s3 object>, <s3 bucket>,
:expires => time_of_exipry)
Ответ 2
Вы можете прочитать файл с S3 и записать его локально в непубличный каталог, а затем использовать X-Sendfile (apache) или X-Accel-Redirect (nginx) для обслуживания содержимого.
Для nginx вы должны включить в конфигурацию что-то вроде следующего:
location /private {
internal;
alias /path/to/private/directory/;
}
Затем в вашем контроллере rails вы выполните следующее:
response.headers['Content-Type'] = your_content_type
response.headers['Content-Disposition'] = "attachment; filename=#{your_file_name}"
response.headers['Cache-Control'] = "private"
response.headers['X-Accel-Redirect'] = path_to_your_file
render :nothing=>true
Хорошая запись процесса здесь
Ответ 3
Метод загрузки полного изображения с использованием файла temp (проверенные рельсы 3.2):
def download
@image = Image.find(params[:image_id])
open(@image.url) {|img|
tmpfile = Tempfile.new("download.jpg")
File.open(tmpfile.path, 'wb') do |f|
f.write img.read
end
send_file tmpfile.path, :filename => "great-image.jpg"
}
end