Node.js'te bir dosyanın boyutunu, türünü (dosya mı klasör mü) ve zaman damgalarını fs.stat() ile öğrenirsiniz; dönen fs.Stats nesnesinde size, isFile(), isDirectory() ve mtime gibi alanlar bulunur. "Dosya var mı?" sorusunun cevabı ise duruma göre değişir: dosyayı hemen ardından açacak, okuyacak ya da yazacaksanız belgeler önceden kontrol yapmamanızı, işlemi doğrudan deneyip ENOENT veya EEXIST hatasını ele almanızı önerir. Yalnızca varlığı bilmek istiyorsanız fs.access() ya da senkron fs.existsSync() kullanılır.
Bu yazıda stat, lstat, access ve existsSync fonksiyonlarını, Stats nesnesinin zaman alanlarının gerçek anlamını, bigint ve v26.2.0 ile gelen Temporal.Instant alanlarını, throwIfNoEntry seçeneğini ve kontrol ile kullanım arasındaki yarış durumunu Node.js v26.13.0 ile çalıştırılmış örneklerle anlatıyoruz. Klasör listeleme için mkdir, readdir ve rm yazımıza bakabilirsiniz.
stat ile dosya bilgisi almak

fsPromises.stat(path) bir fs.Stats nesnesiyle çözülür. Callback biçimi fs.stat(path, (err, stats) => {}), senkron biçimi fs.statSync(path)'tir. Günlük kullanımda en çok ihtiyaç duyulan bilgiler türü soran yöntemler ile bayt cinsinden size alanıdır:
import { stat } from 'node:fs/promises';
const st = await stat('rapor.txt');
console.log(st.isFile(), st.isDirectory());
console.log(st.size);
console.log(st.mtime instanceof Date, typeof st.mtimeMs);
const k = await stat('arsiv');
console.log(k.isFile(), k.isDirectory());
// çıktı: true false
// çıktı: 25
// çıktı: true number
// çıktı: false truerapor.txt dosyasının içeriği Kasım raporu: 12 satış metnidir: 22 karakter ama 25 bayt, çünkü ı (iki kez) ve ş UTF-8'de ikişer bayt tutar. size her zaman bayt sayısıdır, karakter sayısı değildir. Belgeye göre alttaki dosya sistemi boyut bilgisini desteklemiyorsa bu değer 0 olur.
| Alan veya yöntem | Anlamı |
|---|---|
| isFile() | Normal dosya mı |
| isDirectory() | Klasör mü |
| isSymbolicLink() | Sembolik bağlantı mı (yalnızca lstat sonucunda anlamlı) |
| size | Bayt cinsinden boyut |
| mtime / mtimeMs | Verinin son değiştirilme zamanı (Date / milisaniye) |
| atime / atimeMs | Verinin son erişim zamanı |
| ctime / ctimeMs | Dosya durumunun (inode) son değişme zamanı |
| birthtime / birthtimeMs | Oluşturulma zamanı |
| mode | Dosya türü ve izin bitleri |
| ino, dev, nlink, uid, gid | Inode numarası, aygıt, bağlantı sayısı, sahip bilgileri |
Stats nesnelerini kendiniz new ile oluşturmamalısınız; belge genel yapıcının v22.0.0 ve v20.13.0 ile kullanımdan kaldırıldığını (deprecated) belirtir. Nesneler yalnızca stat, lstat, fstat ve senkron karşılıklarından gelir.
Zaman damgaları: ctime oluşturulma zamanı değildir
En sık karıştırılan konu zaman alanlarıdır. Belgedeki tanımlara göre atime verinin son okunduğu, mtime verinin son değiştiği, ctime ise dosyanın durum bilgisinin (inode) son değiştiği zamandır. ctime; chmod, chown, rename, write gibi birçok sistem çağrısıyla güncellenir. Oluşturulma zamanı birthtime'dır. Belge, Node.js 0.12'den önce Windows'ta ctime'ın birthtime değerini tuttuğunu, 0.12'den beri bunun böyle olmadığını ve Unix'te hiçbir zaman öyle olmadığını not eder.
birthtime her dosya sisteminde bulunmaz. Bulunmadığında alan ctime değerini ya da Unix başlangıcını (1970-01-01T00:00Z) taşıyabilir ve bu durumda atime veya mtime'dan büyük görünebilir. Ayrıca milisaniye alanlarının hassasiyeti platforma bağlıdır. Learn rehberi bir adım daha ileri gider: mtime değerini milisaniye hassasiyetle ayarlayıp stat ile okuduğunuzda 1 saniyelik hatta 2 saniyelik çözünürlüğe yuvarlanmış bir değer görebilirsiniz; bazı FAT dosya sistemlerinde atime çözünürlüğü 24 saate kadar çıkar. Bu Node.js hatası değildir, dosya sisteminin sakladığı değerdir.
Pratik sonuç şudur: bir dosyanın değişip değişmediğini anlamak için mtimeMs karşılaştırırken eşitlik beklentinizi dosya sisteminin çözünürlüğüne göre kurun ve oluşturulma zamanına kritik bir iş bağlamayın. Tarih nesneleriyle çalışmanın dil tarafı için Date ve Temporal yazımıza bakabilirsiniz.
bigint ve nanosaniye alanları
stat(yol, { bigint: true }) çağrısı sayısal alanları bigint olarak döndürür ve yalnızca bu kipte bulunan, nanosaniye cinsinden atimeNs, mtimeNs, ctimeNs, birthtimeNs alanlarını ekler. Çok büyük dosya boyutlarında ya da yüksek hassasiyetli zaman karşılaştırmalarında işe yarar:
import { stat } from 'node:fs/promises';
const st = await stat('rapor.txt', { bigint: true });
console.log(typeof st.size, st.size);
console.log(typeof st.mtimeNs);
console.log(st.mtimeNs / 1_000_000n === st.mtimeMs);
// çıktı: bigint 25n
// çıktı: bigint
// çıktı: truebigint değerleri normal sayılarla doğrudan toplayamazsınız; st.size + 1 bir TypeError verir, st.size + 1n yazmanız gerekir. BigInt türünün kuralları için Number ve BigInt yazımıza bakabilirsiniz.
Belgedeki değişiklik kaydına göre v26.2.0 ile Stats nesnesine Temporal.Instant desteği eklendi. Belgedeki örnek çıktıda atimeInstant, mtimeInstant, ctimeInstant ve birthtimeInstant alanları görünür. v26.13.0'da bu alanın türünü kontrol ettiğimizde şu sonucu aldık:
import { stat } from 'node:fs/promises';
const st = await stat('rapor.txt');
console.log(typeof st.mtimeInstant, st.mtimeInstant?.constructor?.name);
// çıktı: object InstantBu alanlar v26.2.0'dan eski sürümlerde yoktur; kodunuz eski sürümlerde de çalışacaksa mtime ya da mtimeMs kullanmaya devam edin.
mode alanı ve dosya izinleri

Stats nesnesinin mode alanı hem dosya türünü hem de izin bitlerini taşır. İzinleri okumak için alt dokuz biti mode & 0o777 ile ayırır ve sekizlik tabanda yazdırırsınız. Belgedeki dosya kipleri tablosuna göre üç basamağın soldaki sahibi, ortadaki grubu, sağdaki diğer kullanıcıları gösterir; 7 okuma, yazma ve çalıştırma, 6 okuma ve yazma, 4 yalnızca okuma demektir. Aşağıdaki örnek bir dosyayı salt okunur yapıp geri alıyor:
import { stat, chmod } from 'node:fs/promises';
console.log(((await stat('rapor.txt')).mode & 0o777).toString(8));
await chmod('rapor.txt', 0o444);
console.log(((await stat('rapor.txt')).mode & 0o777).toString(8));
await chmod('rapor.txt', 0o666);
console.log(((await stat('rapor.txt')).mode & 0o777).toString(8));
// çıktı: 666
// çıktı: 444
// çıktı: 666Bu çıktı Windows'ta alındı. Belge Windows için önemli bir sınır koyar: orada yalnızca yazma izni değiştirilebilir ve sahip, grup, diğerleri ayrımı uygulanmaz; bu yüzden üç basamak her zaman aynı görünür. Linux ve macOS'ta ise dosyanın gerçek sahip, grup ve diğer izinleri ayrı ayrı görünür ve ilk satırın değeri dosyanın oluşturulduğu ortama göre farklı olabilir. İzin denetimini bu bitleri elle yorumlayarak yapmak yerine, sürecin gerçekten erişip erişemeyeceğini aşağıdaki access ile sınamak daha doğrudur.
Dosya yoksa: ENOENT ve throwIfNoEntry
stat olmayan bir yol için ENOENT koduyla hata verir. Yalnızca "varsa bilgisini, yoksa hiçbir şey" istiyorsanız throwIfNoEntry: false seçeneğiyle hata yerine undefined alabilirsiniz. Belgeye göre bu seçenek statSync'te v15.3.0 ve v14.17.0'dan, callback ve Promise biçimlerinde ise v25.7.0'dan beri vardır:
import { stat } from 'node:fs/promises';
import { statSync } from 'node:fs';
try {
await stat('olmayan.txt');
} catch (err) {
console.log(err.code, err.syscall);
}
console.log(statSync('olmayan.txt', { throwIfNoEntry: false }));
console.log(await stat('olmayan.txt', { throwIfNoEntry: false }));
// çıktı: ENOENT stat
// çıktı: undefined
// çıktı: undefinedBelgedeki tanıma göre bu seçenek yalnızca "böyle bir dosya sistemi girdisi yok" durumunu kapsar; başka bir nedenle başarısız olan çağrı hata vermeye devam eder. Bu yüzden undefined sonucunu "dosya yok" olarak, yakalanan hatayı ise "başka bir sorun var" olarak yorumlayabilirsiniz. Hata kodlarının listesi sistem hataları yazımızda bulunuyor.
stat ve lstat farkı: sembolik bağlantılar
stat sembolik bağlantıları takip eder ve bağlantının gösterdiği hedefin bilgisini döndürür. lstat ise bağlantının kendisini inceler. Bu yüzden isSymbolicLink() yalnızca lstat sonucunda true olabilir. Aşağıdaki örnekte arsiv klasörüne bir bağlantı oluşturuyoruz. 'junction' türü Windows'a özgüdür; belgeye göre type parametresi diğer platformlarda yok sayılır, orada normal bir sembolik bağlantı oluşur:
import { symlink, stat, lstat, rm } from 'node:fs/promises';
await rm('kisayol', { force: true });
await symlink('arsiv', 'kisayol', 'junction');
const s = await stat('kisayol');
const l = await lstat('kisayol');
console.log(s.isDirectory(), s.isSymbolicLink());
console.log(l.isDirectory(), l.isSymbolicLink());
// çıktı: true false
// çıktı: false trueWindows'ta dosyalara sembolik bağlantı oluşturmak ek yetki gerektirebildiği için örnekte yalnızca klasörlere bağlanabilen junction türünü seçtik. Bir klasör ağacını gezerken bağlantıları takip etmek istemiyorsanız (örneğin bir yedekleme aracında döngüye girmemek için) lstat kullanın.
access: izin kontrolü
fsPromises.access(path[, mode]) çağıran sürecin dosyaya erişip erişemeyeceğini sınar. Başarılıysa Promise değersiz çözülür, değilse bir hata ile reddedilir; doğrudan true/false döndürmez. mode varsayılan olarak constants.F_OK'tur (yalnızca görünür mü). Okuma, yazma ve çalıştırma için R_OK, W_OK, X_OK değerlerini bit düzeyinde VEYA (|) ile birleştirirsiniz:
import { access, constants } from 'node:fs/promises';
async function erisim(yol, mod) {
try {
await access(yol, mod);
return 'evet';
} catch (err) {
return `hayır (${err.code})`;
}
}
console.log(await erisim('rapor.txt'));
console.log(await erisim('rapor.txt', constants.R_OK | constants.W_OK));
console.log(await erisim('olmayan.txt'));
// çıktı: evet
// çıktı: evet
// çıktı: hayır (ENOENT)Belgede Windows için iki not vardır. X_OK Windows'ta etkisizdir ve F_OK gibi davranır. Ayrıca access klasörlerdeki erişim denetim listelerini (ACL) kontrol etmez; ACL okumayı ya da yazmayı engellese bile yolu erişilebilir olarak bildirebilir. Sabitlerin yeri de değişti: eskiden doğrudan fs.F_OK, fs.R_OK gibi duran sabitler v20.8.0'da kullanımdan kaldırıldı, v25.0.0'da tamamen silindi. Bugün fs.constants ya da fsPromises.constants üzerinden kullanmalısınız.
existsSync ve kullanımdan kalkan fs.exists
fs.existsSync(path) yol varsa true, yoksa false döndürür. Sembolik bağlantıları takip eder; var olmayan bir hedefi gösteren bağlantı için false verir. Dosya ve klasör ayrımı yapmaz:
import { existsSync } from 'node:fs';
console.log(existsSync('rapor.txt'));
console.log(existsSync('arsiv'));
console.log(existsSync('olmayan.txt'));
// çıktı: true
// çıktı: true
// çıktı: falseCallback biçimindeki fs.exists() ise v1.0.0'dan beri kullanımdan kaldırılmış durumdadır (Stability: 0, DEP0034, yalnızca belgede). Nedeni callback'inin Node.js kuralına uymamasıdır: ilk parametre hata değil tek bir boolean'dır. Belge yerine fs.stat() ya da fs.access() önerir. existsSync callback kullanmadığı için kullanımdan kaldırılmamıştır. Ancak ona desteklenmeyen türde bir argüman vermek DEP0187 ile v24.0.0'dan beri çalışma anında uyarı üretir:
import { existsSync } from 'node:fs';
const yol = undefined;
console.log(existsSync(yol));
// çıktı: false
// stderr: (node:92720) [DEP0187] DeprecationWarning: Passing invalid argument types to fs.existsSync is deprecated
// stderr: (Use `node --trace-deprecation ...` to show where the warning was created)Uyarıdaki süreç numarası her çalıştırmada değişir. Belge, ileride bu durumda false yerine hata fırlatılacağını söyler; bu yüzden yolu tanımsız olabilecek bir değişkenden alıyorsanız önce değeri doğrulayın. Kullanımdan kaldırma uyarılarının türleri ve kapatma bayrakları deprecation uyarıları yazımızda anlatılıyor.
Neden önce kontrol edip sonra açmamalısınız?
fs.access, fs.exists ve fs.stat belgelerinin üçü de aynı uyarıyı yapar: dosyayı açmadan, okumadan ya da yazmadan hemen önce varlığını veya erişilebilirliğini kontrol etmek bir yarış durumu (race condition) yaratır. Kontrol ile asıl işlem arasında başka bir süreç dosyayı silebilir, oluşturabilir ya da izinlerini değiştirebilir. Doğru yol, işlemi doğrudan yapmak ve gelen hatayı koduna göre ele almaktır. Belgedeki önerilen yazma deseni 'wx' ile açıp EEXIST yakalamaktır; basit bir kilit dosyası bu desenle şöyle yazılır:
import { open, rm } from 'node:fs/promises';
async function kilitAl(yol) {
try {
const fh = await open(yol, 'wx');
await fh.write(String(process.pid));
await fh.close();
return true;
} catch (err) {
if (err.code === 'EEXIST') return false;
throw err;
}
}
await rm('is.lock', { force: true });
console.log(await kilitAl('is.lock'));
console.log(await kilitAl('is.lock'));
// çıktı: true
// çıktı: falseOkuma için de durum aynıdır: if (existsSync(yol)) readFile(yol) yerine doğrudan okuyup err.code === 'ENOENT' kontrolü yapın. Belge, varlık kontrolünün yalnızca dosyanın kendisi kullanılmayacaksa anlamlı olduğunu söyler; örneğin başka bir sürecin bıraktığı bir işaret dosyasının varlığı bir sinyal olarak kullanılıyorsa. Okuma tarafındaki örnekler dosya okuma yazımızda, 'wx' bayrağı dosya yazma yazımızda anlatılıyor.
Hangi fonksiyonu ne zaman kullanmalı?

| İhtiyaç | Önerilen | Not |
|---|---|---|
| Dosyayı okuyacağım | Doğrudan readFile, ENOENT yakala | Önceden kontrol yarış durumu yaratır |
| Dosya yoksa oluşturacağım | open veya writeFile ile 'wx' | EEXIST ile var olduğunu anlarsınız |
| Boyut, tür, tarih bilgisi | stat | Bağlantıyı takip eder |
| Bağlantının kendisi | lstat | isSymbolicLink burada anlamlı |
| Yalnızca var mı (kullanmayacağım) | access veya existsSync | access hata ile, existsSync boolean ile cevap verir |
| Yoksa hata istemiyorum | stat ile throwIfNoEntry: false | Diğer hatalar yine fırlatılır |
Sık yapılan hatalar
- ctime'ı oluşturulma zamanı sanmak:
ctimedurum değişikliği zamanıdır; oluşturulma içinbirthtimevardır ve her dosya sisteminde güvenilir değildir. - access'in boolean döndüğünü sanmak:
await access(yol)başarıdaundefinedverir, başarısızlıkta hata fırlatır.if (await access(yol))her zaman yanlış çalışır. - existsSync ardından açmak: iki çağrı arasında durum değişebilir; doğrudan açıp hatayı ele alın.
- fs.F_OK kullanmak: bu sabitler v25.0.0'da
fsüzerinden kaldırıldı;fs.constants.F_OKyazın. - stat ile bağlantı tespit etmeye çalışmak:
statbağlantıyı takip ettiği içinisSymbolicLink()hepfalsedöner;lstatkullanın. - size değerini karakter sayısı sanmak:
sizebayt sayısıdır; Türkçe karakterler UTF-8'de birden fazla bayt tutar. - bigint ile normal sayıyı karıştırmak:
bigint: truekipinde aritmetik işlemler için diğer işlenen debigintolmalıdır.
Sık Sorulan Sorular
Node.js'te dosya var mı nasıl kontrol edilir?
Dosyayı ardından kullanacaksanız kontrol etmeyin, doğrudan kullanın ve ENOENT hatasını yakalayın. Yalnızca varlığı bilmeniz gerekiyorsa senkron kodda existsSync(yol), async kodda access(yol) ya da stat(yol, { throwIfNoEntry: false }) kullanın.
fs.existsSync kullanımdan kaldırıldı mı?
Hayır. Kullanımdan kaldırılan callback biçimindeki fs.exists()'tir (DEP0034). existsSync geçerlidir; yalnızca ona string, Buffer veya URL dışında bir tür vermek DEP0187 uyarısı üretir.
Bir yolun klasör olup olmadığını nasıl anlarım?
(await stat(yol)).isDirectory() kullanın. Bir klasörün içeriğini gezerken her girdi için stat çağırmak yerine readdir(yol, { withFileTypes: true }) ile gelen Dirent nesnelerinin isDirectory() yöntemini kullanabilirsiniz.
Dosya boyutunu MB olarak nasıl gösteririm?
stat ile aldığınız size bayt cinsindendir; (st.size / 1024 / 1024).toFixed(2) ile megabayt (MiB) değerine çevirebilirsiniz. Çok büyük dosyalarda hassasiyet kaybı yaşamamak için bigint: true seçeneği vardır.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Node.js öğrenme rehberi · Dosya okuma · Klasör işlemleri · fs.watch ile izleme · Sistem hataları · Node.js nedir