Использование 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