2009-08-24 2 views
11

В моем приложении у меня есть требование, которое меня точит.Использование Send_File для удаленного источника (Ruby on Rails)

У меня есть файл, хранящийся в S3, и когда пользователь нажимает на ссылку в моем приложении, я вхожу в базу данных, которую они кликнули по ссылке, уменьшив уровень «загрузки кредита» на один, а затем я хочу запросите файл для загрузки.

Я не просто хочу, чтобы перенаправить пользователя к файлу, так как он хранится в S3, и я не хочу, чтобы они имели связь исходного файла (так что я могу поддерживать целостность и доступ)

Похоже, send_file() не работает с удаленным исходным файлом, кто-нибудь порекомендует драгоценный камень или подходящий код, который это сделает?

+0

Возможный дубликат [как заставить отправить \ _data для загрузки файла в браузере?] (Http://stackoverflow.com/questions/7337508/how-to-force-send-data-to-download- the-file-in-the-browser) –

ответ

9

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

нотабене Я бы не советовал подавать файл из самого процесса рельсов. Если возможно/приемлемо для вашего случая использования, я бы воспользовался аутентифицированным GET, чтобы обслуживать личные данные из ведра.

Используя аутентифицированный GET, вы можете хранить ведро и его объекты в закрытом виде, предоставляя временное разрешение для чтения определенного содержимого объекта, создавая URL-адрес, содержащий токен подписи аутентификации. Пользователь просто перенаправляется на аутентифицированный URL-адрес, а токен может быть действителен всего несколько минут.

Используя упомянутую выше AWS :: S3 вы можете получить заверенную GET URL следующим образом:

time_of_exipry = Time.now + 2.minutes 
S3Object.url_for(<s3 object>, <s3 bucket>, 
        :expires => time_of_exipry) 
+0

S3Ojbect.url_for выглядит как лучший подход – klochner

+2

Мы использовали перенаправление на год, но теперь мы действительно хотим, чтобы Safari/Mac работал: http://stackoverflow.com/questions/1995589/html5-audio-safari-live-broadcast-vs-not send_file_headers!является приватным, ни send_data, либо send_file не принимает удаленные файлы; обходным, но просто FYI – kain

+0

Я думаю, что способ генерации time_of_expiry на самом деле не работает, вам нужно сделать: 'time_of_expiry = 2.minutes.from_now.to_i' – Raphael

3

Вы можете прочитать файл с S3 и записать его локально в непубличной каталог, а затем использовать X-Sendfile (apache) или X-Accel-Redirect (nginx) для обслуживания содержимого.

Для Nginx вы должны включить что-то вроде следующего в вашей конфигурации:


      location /private { 
           internal; 
           alias /path/to/private/directory/; 
      } 

Тогда в контроллере рельсы, вы делаете следующее:


    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 

Хорошая рецензия процесса является here

+0

то, что вы говорите, правильно, но может ли быть какой-либо способ сделать это, если файлы не являются локальными веб-сервер? Проблема в том, что файлы хранятся в веб-службе S3. – LucaM

+1

Я не думаю, что вы можете предоставить людям доступ к вашим S3-файлам для каждого пользователя, если у них нет собственной учетной записи S3. Вам нужно будет загрузить файлы с S3 на ваш локальный сервер и отправить оттуда. – klochner

+0

Файлы s3 часто являются общедоступными, только используя обфускационные URI. Похоже, эффект, который вы пытаетесь достичь, можно сделать без копирования файлов, см. Обсуждение здесь: http://www.ruby-forum.com/topic/132214. Вам нужно будет сделать 2 этапа: вход в систему, а затем отправьте запрос файла. – austinfromboston

3

Метод полной загрузки изображения с использованием временного файла (проверенные рельсы 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 
+0

Работает как шарм! Tempfile автоматически удаляется на GC! –

Смежные вопросы