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

stat ile dosya bilgisi almak: isFile(), isDirectory(), isSymbolicLink(), size

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:

javascript
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 true

rapor.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öntemAnlamı
isFile()Normal dosya mı
isDirectory()Klasör mü
isSymbolicLink()Sembolik bağlantı mı (yalnızca lstat sonucunda anlamlı)
sizeBayt cinsinden boyut
mtime / mtimeMsVerinin son değiştirilme zamanı (Date / milisaniye)
atime / atimeMsVerinin son erişim zamanı
ctime / ctimeMsDosya durumunun (inode) son değişme zamanı
birthtime / birthtimeMsOluşturulma zamanı
modeDosya türü ve izin bitleri
ino, dev, nlink, uid, gidInode 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:

javascript
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ı: true

bigint 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:

javascript
import { stat } from 'node:fs/promises';

const st = await stat('rapor.txt');
console.log(typeof st.mtimeInstant, st.mtimeInstant?.constructor?.name);
// çıktı: object Instant

Bu 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

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:

javascript
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ı: 666

Bu çı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:

javascript
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ı: undefined

Belgedeki 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:

javascript
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 true

Windows'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:

javascript
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:

javascript
import { existsSync } from 'node:fs';

console.log(existsSync('rapor.txt'));
console.log(existsSync('arsiv'));
console.log(existsSync('olmayan.txt'));
// çıktı: true
// çıktı: true
// çıktı: false

Callback 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:

javascript
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:

javascript
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ı: false

Okuma 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ı?

Hangi fonksiyonu ne zaman kullanmalı? Dosyayı okuyacağım, Dosya yoksa oluşturacağım, Boyut, tür, tarih bilgisi, Bağlantının kendisi
İhtiyaçÖnerilenNot
Dosyayı okuyacağımDoğrudan readFile, ENOENT yakalaÖnceden kontrol yarış durumu yaratır
Dosya yoksa oluşturacağımopen veya writeFile ile 'wx'EEXIST ile var olduğunu anlarsınız
Boyut, tür, tarih bilgisistatBağlantıyı takip eder
Bağlantının kendisilstatisSymbolicLink burada anlamlı
Yalnızca var mı (kullanmayacağım)access veya existsSyncaccess hata ile, existsSync boolean ile cevap verir
Yoksa hata istemiyorumstat ile throwIfNoEntry: falseDiğer hatalar yine fırlatılır

Sık yapılan hatalar

  • ctime'ı oluşturulma zamanı sanmak: ctime durum değişikliği zamanıdır; oluşturulma için birthtime vardır ve her dosya sisteminde güvenilir değildir.
  • access'in boolean döndüğünü sanmak: await access(yol) başarıda undefined verir, 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_OK yazın.
  • stat ile bağlantı tespit etmeye çalışmak: stat bağlantıyı takip ettiği için isSymbolicLink() hep false döner; lstat kullanın.
  • size değerini karakter sayısı sanmak: size bayt sayısıdır; Türkçe karakterler UTF-8'de birden fazla bayt tutar.
  • bigint ile normal sayıyı karıştırmak: bigint: true kipinde aritmetik işlemler için diğer işlenen de bigint olmalı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