2009-08-24 13 views
11

W mojej aplikacji mam wymaganie, które mnie oszuka.Używanie Send_File do zdalnego źródła (Ruby on Rails)

Mam plik zapisany w S3, a gdy użytkownik kliknie na link w moim app, loguję w DB oni kliknął w link, zmniejszyć ich 'Pobrać kredytowej dodatek przez jednego, a potem chcę poproś o plik do pobrania.

nie po prostu chcesz przekierować użytkownika do pliku, ponieważ jest on przechowywany w S3 i nie chcę ich mieć link do pliku źródłowego (tak, że mogę utrzymać integralność i dostępu)

Wygląda na to, że send_file() nie będzie działać ze zdalnym plikiem źródłowym, czy ktoś poleci klejnot lub odpowiedni kod, który to zrobi?

+0

możliwy duplikat [jak wymusić wysłanie \ _data, aby pobrać plik w przeglądarce?] (Http://stackoverflow.com/questions/7337508/how-to-force-send-data-to-download- plik-w-przeglądarce) –

Odpowiedz

9

Musisz przesłać strumieniowo zawartość pliku do użytkownika podczas odczytu z wiadra/obiektu S3.

Jeśli używasz coś Biblioteka AWS::S3 jak to może działać:

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 
} 

Kod ten jest najczęściej kopiowane formularz na send_file kod, który sam w sobie działa tylko dla plików lokalnych lub plikopodobny obiektów

nb W każdym razie odradzam podawanie pliku z samego procesu railsowego. Jeśli to możliwe/akceptowalne dla twojego przypadku użycia, użyłbym uwierzytelnionego GETGET, aby podać prywatne dane z wiadra.

Korzystając z uwierzytelnionego polecenia GET, można zachować zasobnik i jego obiekty jako prywatne, jednocześnie umożliwiając tymczasowe zezwolenie na odczytanie określonej treści obiektu poprzez tworzenie adresu URL zawierającego token uwierzytelniania podpisu. Użytkownik jest po prostu przekierowywany na uwierzytelniony adres URL, a token może być ważny tylko przez kilka minut.

Korzystanie z ww AWS :: S3 można uzyskać uwierzytelnioną url dostać się w ten sposób:

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

S3Ojbect.url_for wygląda jak najlepsze podejście – klochner

+2

Użyliśmy przekierowania na rok, ale teraz naprawdę chcemy, aby Safari/Mac działał: http://stackoverflow.com/questions/1995589/html5-audio-safari-live-broadcast-vs-not send_file_headers!jest prywatny, ani send_data, ani send_file nie akceptuje plików zdalnych; możliwe do obejścia, ale tylko FYI – kain

+0

Myślę, że sposób generowania time_of_expiry faktycznie nie działa, musisz zrobić: 'time_of_expiry = 2.minutes.from_now.to_i' – Raphael

3

można odczytać plik z S3 i zapisać go lokalnie do katalogu niepublicznej, a następnie użyć X-Sendfile (apache) lub X-Accel-Redirect (nginx) do obsługi treści.

Dla nginx byś to coś jak następuje w config:


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

Następnie w kontroler szyny, należy wykonać następujące czynności:


    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 

Dobrym writeup procesu jest here

+0

to, co mówisz, jest poprawne, ale czy byłby jakikolwiek sposób, aby zrobić to bez lokalnych plików? serwer internetowy? Problem w tym przypadku polega na tym, że pliki są przechowywane w serwisie WWW S3. – LucaM

+1

Nie sądzę, aby można było przyznawać poszczególnym użytkownikom bezpośredni dostęp do plików S3, chyba że mają oni własne konto S3. Będziesz musiał pobrać pliki z S3 na twój lokalny serwer i wysłać tam. – klochner

+0

Pliki s3 są często publiczne, po prostu używając zaciemnionych URI. Wydaje się, że efekt, który próbujesz osiągnąć, można zrobić bez kopiowania plików, zobacz dyskusję tutaj: http://www.ruby-forum.com/topic/132214. Będziesz musiał jednak wykonać 2 etapy: zaloguj się, a następnie wyślij żądanie pliku. – austinfromboston

3

Metoda pobierania całego obrazu z użyciem pliku tymczasowego (testowane szyny 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

Działa jak czar! Tempfile jest automatycznie usuwany w GC! –