İç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ız 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üyorsunuz. Büyük dosyada doğru model “oku-bas-oku-bas”; üretirken aktarırsınız, bellek düz kalır.

  1. PHP’den geçirecekseniz chunk-by-chunk stream edin. Laravel’de Storage::readStream() ile dosyayı açın, bir StreamedResponse (ya da response()->streamDownload) döndürün ve callback içinde bir döngüde fread edip yazıp flush() çağırın. Böylece bellek aynı anda yalnızca tek chunk kadar (birkaç MB) dolu kalır. Content-Length ve Content-Disposition header’larını verin ki tarayıcı indirme penceresini ve ilerleme çubuğunu doğru göstersin.
  2. Output buffering’i o route için kapatın. 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ın; X-Accel-Buffering: no header’ı Nginx’e “bunu biriktirme” der.
  3. Octane’de cevabı bellekte tutmayın. Kalıcı süreçte (Octane/Swoole) koca bir response gövdesini worker’da biriktirirseniz bellek istekler arası temizlenmez ve süreç şişer. Stream’i doğrudan çıkışa akıtın, ara değişkende tutmayın.
  4. En iyisi: baytı uygulamadan hiç geçirmeyin. Ölçekte doğru cevap PHP’yi veri yolundan çıkarmak. Dosya S3/object storage’daysa kısa ömürlü bir presigned URL üretin, 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ız readStream + StreamedResponse ile chunk’layın, output buffering’i kapatmayı unutmayın. Tek kural: 5 GB’ı asla tek seferde belleğe almayın — ne PHP’de, ne de aradaki katmanda.

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