- Konu sahibi
- #1
İndirilebilir bir dosya sunmak için Rails’te en pratik yöntem, bir controller action içinde
Örneğin
Burada
Bu tanımdan sonra
Bir görünümde indirme bağlantısı vermek için:
Dosya mevcutsa tarayıcı indirme işlemini başlatır. Dosya yoksa controller’ın
PDF veya görsel gibi bazı dosya türleri tarayıcıda açılabilir. Kullanıcıya indirme penceresi göstermek istiyorsanız
Dosya diskte hazır değilse veya içerik istek sırasında oluşturuluyorsa
Bu yöntemde
Bu ayrım önemlidir: Dosyanın yalnızca oturum açmış veya belirli yetkiye sahip kullanıcılar tarafından indirilmesi gerekiyorsa doğrudan
Örneğin yalnızca oturum açmış kullanıcıların dosya indirebildiği basit bir action şöyle olabilir:
Gerçek uygulamada yetki koşulunu uygulamanızın kimlik doğrulama ve yetkilendirme yapısına göre kurun. Kullanıcıdan gelen dosya adını doğrudan
İndirme bağlantısına tıkladığınızda beklenen dosya adıyla indirme başlaması, action’ın çalıştığını ve yanıt başlıklarının uygun olduğunu gösterir. 404 yanıtı dosyanın beklenen konumda bulunmadığını; 401 veya 403 yanıtı ise kimlik ya da yetki kontrolünün isteği reddettiğini gösterir. Bu sonuçlar tek başına dosyanın bozuk olduğunu kanıtlamaz. Dosya indirildiği hâlde açılamıyorsa gerçek dosya türünü,
send_file kullanıp bu action’a bir rota tanımlamaktır. Dosya uygulamanın storage veya public altında tutuluyorsa, indirme bağlantısı bu rotaya yönlendirilir; istek geldiğinde Rails dosyayı HTTP yanıtı olarak gönderir.Controller içinde dosya indirme action’ı oluşturma
Örneğin
storage/downloads/rapor.pdf konumunda bulunan bir dosyayı indirmek için ilgili controller’a şu action’ı ekleyin:
Ruby:
class DownloadsController < ApplicationController
def report
file_path = Rails.root.join("storage", "downloads", "rapor.pdf")
unless File.file?(file_path)
head :not_found
return
end
send_file file_path,
filename: "rapor.pdf",
type: "application/pdf",
disposition: "attachment"
end
end
Burada
Rails.root, uygulamanızın ana dizinini gösterir. File.file? kontrolü dosyanın gerçekten mevcut ve dosya türünde olup olmadığını denetler. Dosya bulunamazsa 404 Not Found yanıtı gönderilir; bu, indirme işleminin başarısız olduğu anlamına gelir, ancak dosya yolunun dışarıya sızdığı anlamına gelmez.disposition: "attachment" tarayıcıya içeriği sayfada göstermeye çalışmak yerine indirme olarak ele almasını söyler. filename kullanıcının indireceği dosyanın adını, type ise dosyanın MIME türünü belirler.Action için rota tanımlama
config/routes.rb dosyasına controller action’ını çağıran bir rota ekleyin:
Ruby:
Rails.application.routes.draw do
get "downloads/report", to: "downloads#report", as: :download_report
end
Bu tanımdan sonra
download_report_path yardımcı metodu oluşur. Uygulama çalışırken tarayıcıdan /downloads/report adresine gidildiğinde Rails, DownloadsController#report action’ını çalıştırır ve dosyayı gönderir.Bir görünümde indirme bağlantısı vermek için:
Kod:
<%= link_to "Raporu indir", download_report_path %>
Dosya mevcutsa tarayıcı indirme işlemini başlatır. Dosya yoksa controller’ın
head :not_found satırı nedeniyle 404 yanıtı alırsınız. Bu durumda önce beklenen dosyanın gerçekten storage/downloads/rapor.pdf altında bulunduğunu ve uygulamanın çalıştığı ortamda erişilebilir olduğunu kontrol edin.Dosyayı tarayıcıda göstermek yerine indirme olarak sunma
PDF veya görsel gibi bazı dosya türleri tarayıcıda açılabilir. Kullanıcıya indirme penceresi göstermek istiyorsanız
disposition: "attachment" kullanın. Dosyanın tarayıcıda açılmasına izin vermek istiyorsanız bu değeri inline yapabilirsiniz:
Ruby:
send_file file_path,
filename: "rapor.pdf",
type: "application/pdf",
disposition: "inline"
inline kullanılması dosyanın kesin olarak gösterileceğini garanti etmez; sonuç tarayıcının ve dosya türünün desteklenmesine bağlıdır. Kesin indirme davranışı için attachment tercih edilir.Bellekte oluşturulan içeriği indirme
Dosya diskte hazır değilse veya içerik istek sırasında oluşturuluyorsa
send_data kullanabilirsiniz. Örneğin varsayımsal bir CSV çıktısı şu şekilde gönderilebilir:
Ruby:
def users_csv
csv_content = User.order(:id).pluck(:id, :email).map do |id, email|
"#{id},#{email}"
end.join("\n")
send_data csv_content,
filename: "kullanicilar.csv",
type: "text/csv",
disposition: "attachment"
end
Bu yöntemde
send_data dosya yolu değil, bellekteki metin veya ikili veriyi yanıt olarak gönderir. Büyük dosyaları tamamen bellekte oluşturmak RAM kullanımını artırabileceği için bu yaklaşım küçük ve orta boyutlu çıktılarda daha uygundur. Büyük veya önceden oluşturulmuş dosyalarda send_file kullanmak daha doğru bir seçimdir.| İhtiyaç | Kullanılacak yöntem | Temel fark |
|---|---|---|
| Diskte bulunan dosyayı göndermek | send_file | Dosya yolundan yanıt üretir |
| Bellekte oluşturulan içeriği göndermek | send_data | Metin veya ikili veriyi doğrudan gönderir |
| Herkese açık sabit dosya | Doğrudan statik dosya bağlantısı | Controller yetkilendirmesi çalışmaz |
public dizinine bağlantı vermek yerine controller action’ı kullanın. Controller içinde yetki kontrolünü send_file çağrısından önce yapın.Yetkili kullanıcılar için güvenli indirme
Örneğin yalnızca oturum açmış kullanıcıların dosya indirebildiği basit bir action şöyle olabilir:
Ruby:
def private_report
return head :unauthorized unless current_user
file_path = Rails.root.join("storage", "reports", "rapor.pdf")
return head :not_found unless File.file?(file_path)
send_file file_path,
filename: "rapor.pdf",
type: "application/pdf",
disposition: "attachment"
end
Gerçek uygulamada yetki koşulunu uygulamanızın kimlik doğrulama ve yetkilendirme yapısına göre kurun. Kullanıcıdan gelen dosya adını doğrudan
Rails.root.join içine eklemeyin; ../ gibi yol parçaları, uygulama dışındaki dosyalara erişim riski oluşturabilir. Kullanıcı seçimi gerekiyorsa veritabanındaki güvenilir bir kayıtla dosya eşleştirin veya izin verilen dosya adlarını sınırlı bir listeyle doğrulayın.İndirme bağlantısına tıkladığınızda beklenen dosya adıyla indirme başlaması, action’ın çalıştığını ve yanıt başlıklarının uygun olduğunu gösterir. 404 yanıtı dosyanın beklenen konumda bulunmadığını; 401 veya 403 yanıtı ise kimlik ya da yetki kontrolünün isteği reddettiğini gösterir. Bu sonuçlar tek başına dosyanın bozuk olduğunu kanıtlamaz. Dosya indirildiği hâlde açılamıyorsa gerçek dosya türünü,
type değerini ve dosyanın eksik veya sıfır boyutlu olup olmadığını ayrıca kontrol edin.