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

> 2-5 GB dosyayı presigned URL ya da X-Accel-Redirect ile PHP'nin dışından servis edin; zorundaysanız readStream ile chunk'layıp buffering'i kapatın.

- Soruldu: 2026-05-22
- Yanıtlandı: 2026-05-25
- Soran: Barış
- Etiketler: performans, laravel, altyapi
- Kaynak: https://muhammetsafak.com/tr/sor-bakalim/buyuk-dosyalari-php-bellegini-sismeden-stream-ile-indirmek/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
**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?


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.

## Kısa cevap

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. Dosyayı uygulamanın diskinden çıkarma kararının kendisini [yerel diskten S3'e geçiş kaydında](/tr/sor-bakalim/yerel-diskten-s3-e-gecerken-efs-ara-cozum-mu/) ayrıca ele almıştım.

## Neden

1. **Bellek limiti bir tavan değil, tasarımın sonucudur.** `memory_limit`'i yükseltmek 5 GB'lık bir dosyayı 5 GB'lık bir RAM sorununa çevirir; model değişmedikçe sınır hep geri gelir.

2. **Ara katmanlar stream'i sessizce iptal eder.** PHP'de doğru yazsanız bile FastCGI/Nginx buffering açıksa dosya yeniden belleğe birikir.

3. **En ölçeklenebilir yol, uygulamayı veri yolundan çıkarmaktır.** Yetkiyi PHP verir, baytı web sunucusu ya da object storage taşır.

## Ne yapmalı

1. **En iyisi: baytı uygulamadan hiç geçirmeyin.** 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.

2. **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 döngüde `fread` edip yazıp `flush()` çağırın. `Content-Length` ve `Content-Disposition` header'larını verin.

3. **Output buffering'i o route için kapatın.** PHP'de `ob_*`, Nginx'te `fastcgi_buffering`/`proxy_buffering` kapalı olmalı; `X-Accel-Buffering: no` header'ı Nginx'e "bunu biriktirme" der.

4. **Octane'de cevabı bellekte tutmayın.** Kalıcı süreçte koca bir response gövdesini worker'da biriktirirseniz bellek istekler arası temizlenmez. Stream'i doğrudan çıkışa akıtın, ara değişkende tutmayın.

**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.

## İlgili Yazılar

- [İşlediğim satırları aynı anda güncellerken chunk yerine chunkById mı kullanmalıyım?](https://muhammetsafak.com/tr/sor-bakalim/isledigim-satirlari-ayni-anda-guncellerken-chunk-yerine-chunkbyid-mi-kullanmaliyim/) — Sor Bakalım
- [Eloquent'te N+1'i erken yakalamak için Model::preventLazyLoading'i yalnızca lokalde mi yoksa production'da da mı açmalıyım?](https://muhammetsafak.com/tr/sor-bakalim/eloquentte-n-1i-erken-yakalamak-icin-model-preventlazyloadingi-yalnizca-lokalde-mi/) — Sor Bakalım
- [API sunucumu Cloudflare Tunnel arkasına alıp 443'ü internete kapatmalı mıyım?](https://muhammetsafak.com/tr/sor-bakalim/api-sunucusunu-cloudflare-tunnel-arkasina-mi-almali/) — Sor Bakalım
