İçeriğe geç
Muhammet Şafak
en
Soran: Barış Cevaplandı:

2-5 GB dosyaları PHP belleğini şişirmeden stream ile nasıl indirtirim?


Soru

Kullanıcılar 2-5 GB'lık log arşivlerini ZIP olarak indiriyor. PHP'de dosyayı `file_get_contents()` ya da `Storage::get()` ile okuyup döndürmeye çalışınca `memory_limit` aşılıyor ve istek patlıyor. Belleği birkaç megabaytta sabitleyip dosyayı diskten chunk-by-chunk okuyup doğrudan HTTP response stream'ine basan yapıyı nasıl kurarım?

Cevap

Kısa cevap: file_get_contents/Storage::get dosyanın TAMAMINI belleğe alır; 2-5 GB için yapman gereken onu stream etmek — ama daha iyisi, baytı PHP’den hiç geçirmemek.

Sorun kavramsal: bir HTTP cevabını “önce tamamını hazırla, sonra gönder” diye düşünüyorsun. Büyük dosyada doğru model “oku-bas-oku-bas”; üretirken aktarırsın, bellek düz kalır.

  1. PHP’den geçireceksen chunk-by-chunk stream et. Laravel’de Storage::readStream() ile dosyayı aç, bir StreamedResponse (ya da response()->streamDownload) döndür ve callback içinde bir döngüde fread edip yazıp flush() çağır. Böylece bellek aynı anda yalnızca tek chunk kadar (birkaç MB) dolu kalır. Content-Length ve Content-Disposition header’larını ver ki tarayıcı indirme penceresini ve ilerleme çubuğunu doğru göstersin.
  2. Output buffering’i o route için kapat. Asıl tuzak burada: PHP’de ob_*, FastCGI/Nginx tarafında fastcgi_buffering/proxy_buffering açıksa katman dosyayı yeniden belleğe biriktirip stream’i iptal eder. O endpoint’te buffering’i kapat; X-Accel-Buffering: no header’ı Nginx’e “bunu biriktirme” der.
  3. Octane’de cevabı bellekte tutma. Kalıcı süreçte (Octane/Swoole) koca bir response gövdesini worker’da biriktirirsen bellek istekler arası temizlenmez ve süreç şişer. Stream’i doğrudan çıkışa akıt, ara değişkende tutma.
  4. En iyisi: baytı uygulamadan hiç geçirme. Ölçekte doğru cevap PHP’yi veri yolundan çıkarmak. Dosya S3/object storage’daysa kısa ömürlü bir presigned URL üret, kullanıcı doğrudan oradan indirsin. Diskteyse X-Accel-Redirect (Nginx) veya X-Sendfile (Apache) ile dosyayı web sunucusu servis etsin; uygulama yalnızca yetkiyi kontrol edip header’ı döner.

Sonuç: Ben olsam ilk tercih presigned URL ya da X-Sendfile/X-Accel-Redirect olurdu — PHP yetkilendirir, baytı taşımaz; en ucuz, en ölçeklenebilir yol. Dosyayı gerçekten PHP üzerinden vermek zorundaysan readStream + StreamedResponse ile chunk’la, output buffering’i kapatmayı unutma. Tek kural: 5 GB’ı asla tek seferde belleğe alma — ne PHP’de, ne de aradaki katmanda.

Etiketler: #performans#laravel#altyapı
Paylaş:

Yorumlar

Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.

Diğer Sorular

Tüm sorular

Sitede Ara

Yazı, proje ve sayfalarda arama yapmak için yazmaya başlayın.

Esc ile kapat Pagefind ile güçlendirildi