Node.js ile File Streaming: Büyük Dosyalar, Bellek ve Performans
fs.readFile ile on gigabaytlık bir dosyayı okumak, süreç belleğini tek seferde doldurmaya davetiye çıkarır. Stream ise veriyi parça parça taşır: bellek kullanımı sınırlı kalır, gecikme (latency) erken başlar ve tüketici hazır oldukça üretici yavaşlayabilir — buna backpressure denir.
Bu yazıda Node.js’te dosya akışlarının temelini ve üretimde sık kullanılan kalıpları özetliyoruz.
Stream Nedir?
Node.js’te stream, sınırlı boyutta chunk’lar halinde akan veri kanalıdır. Türler:
| Tür | Rol |
|---|---|
| Readable | Okur (ör. dosya, HTTP yanıtı) |
| Writable | Yazar (ör. disk, socket) |
| Duplex | İki yönlü (ör. TCP) |
| Transform | Okur, dönüştürür, yazar (ör. gzip) |
Dosya tarafında en sık kullanılanlar: fs.createReadStream, fs.createWriteStream.
readFile Yerine createReadStream
import fs from 'node:fs';
// Kötü: tüm dosya RAM’e
// const buf = fs.readFileSync('huge.bin');
const rs = fs.createReadStream('huge.bin', { highWaterMark: 64 * 1024 });
rs.on('data', (chunk) => {
// chunk: Buffer veya string (encoding verilirse)
});
rs.on('end', () => console.log('bitti'));
rs.on('error', (err) => console.error(err));
highWaterMark dahili tampon boyutunu etkiler; çoğu senaryoda varsayılan yeterlidir.
pipe ve pipeline
Eski kalıp readable.pipe(writable) hâlâ çalışır; modern ve önerilen yol stream.promises.pipeline veya stream.pipeline ile hata iletimi ve temiz kapanıştır:
import fs from 'node:fs';
import { pipeline } from 'node:stream/promises';
import zlib from 'node:zlib';
await pipeline(
fs.createReadStream('input.log'),
zlib.createGzip(),
fs.createWriteStream('input.log.gz')
);
pipeline zincirdeki hatayı yakalar ve stream’leri güvenli biçimde sonlandırmaya çalışır.
Backpressure (Basınç)
Okuyucu yavaş, yazıcı hızlıysa Writable tamponu dolar. Node.js bu durumda Readable’ı geçici olarak durdurur — bellek taşması önlenir. pipe bunu yönetir; manuel write kullanıyorsanız stream.write() dönüş değerini ve drain olayını dinlemeniz gerekir.
HTTP ile Birlikte
Büyük dosyayı istemciye gönderirken:
import fs from 'node:fs';
import http from 'node:http';
http.createServer((req, res) => {
const stream = fs.createReadStream('report.pdf');
res.setHeader('Content-Type', 'application/pdf');
stream.pipe(res);
stream.on('error', () => res.destroy());
}).listen(3000);
Üretimde pipeline(fs.createReadStream(...), res) tercih edin; hata ve kapanış tek yerde toplanır.
Sıkıştırma Zinciri
Log arşivleme veya statik varlık sunumu için:
await pipeline(
fs.createReadStream('data.json'),
zlib.createGzip(),
fs.createWriteStream('data.json.gz')
);
Transform katmanı eklemek (ör. satır satır JSON parse) için stream.Transform veya readline ile createInterface birlikte kullanılabilir.
Hata Yönetimi
pipeline/finishedkullanın; yalnızpipeile unutulanerrordinleyicileri yarı kalmış stream bırakır.- HTTP yanıtında hata olursa bağlantıyı
res.destroy(err)ile kapatın.
Ne Zaman Stream?
- Dosya RAM’den büyük veya boyutu önceden bilinmiyorsa
- Erken tüketim gerekiyorsa (ilk byte’lar gelir gelmez işlem)
- Zincirleme dönüşüm (şifreleme, sıkıştırma, parse)
Ne Zaman Buffer?
- Küçük yapılandırma dosyaları
- Tüm içeriğin atomik olarak gerekli olduğu testler
Özet
Stream, Node.js’in I/O verimliliğinin kalbidir. Dosya + HTTP + sıkıştırma üçlüsünde pipeline ile hataları tek noktada toplayın; readFile ile büyük dosyayı “kopyalamak” yerine akışı tercih edin.
İyi kodlamalar.