Tarayıcıda kullanıcının seçtiği bir dosyayı okumak için <input type="file"> elemanının files listesinden bir File nesnesi alır, içeriğini await file.text() veya file.arrayBuffer() ile okursunuz. Eski ama hâlâ yaygın yol FileReader ile olay tabanlı okumadır. Dosyayı bir <img> veya bağlantıda göstermek için URL.createObjectURL(file) ile geçici bir blob: adresi üretir, işiniz bitince URL.revokeObjectURL() ile serbest bırakırsınız.

Bu yazıda File API'nin temel parçalarını, yani Blob, File, FileList, FileReader ve nesne URL'lerini, dosya seçme ve sürükle bırak akışlarını, önizleme göstermeyi ve sık yapılan hataları anlatıyoruz. Blob ve File örnekleri tarayıcıyla aynı standardı uygulayan Node.js 24 ile çalıştırılıp çıktıları doğrulandı. Dizinin haritası için Web API öğrenme rehberi sayfasına bakabilirsiniz.

File API neyi kapsar?

File API neyi kapsar? Blob, File, FileList, FileReader

File API, web uygulamalarının dosyalara ve içeriklerine erişmesini sağlar; ama yalnızca kullanıcının açıkça verdiği dosyalara. Dosyalar ya bir dosya <input> elemanıyla seçilir ya da sürükle bırak ile sayfaya bırakılır. Bu yollarla gelen dosya kümeleri FileList nesneleridir ve tek tek File nesnelerini verir. FileReader, MDN'in vurguladığı gibi, kullanıcının diskindeki bir dosyayı yol adıyla okuyamaz; yalnızca kullanıcının seçtiği dosyaları okuyabilir. Sunucudaki dosyaları okumak için fetch() kullanılır.

MDN dosyalarla ilgili iki API'yi daha ayırır. File and Directory Entries API yine form veya sürükle bırak ile gelen dosya ve klasörleri işler ve büyük ölçüde webkit önekli eklentilerden oluşur. File System API ise kökene özel bir sanal dosya sistemi (OPFS) sağlar ve form elemanlarıyla ilgisi yoktur; buna Storage API ve OPFS yazısında kısaca değiniyoruz.

ArayüzNe işe yarar
BlobDeğiştirilemez ham veri; metin veya ikili olarak okunur, akışa çevrilebilir
FileBlob'un özel bir türü; ad ve son değiştirilme zamanı ekler
FileListinput.files veya sürükle bırak ile gelen dosya listesi
FileReaderBlob veya File içeriğini olaylarla eş zamansız okur
FileReaderSyncAynı işi eş zamanlı yapar; yalnızca Web Worker içinde
URL.createObjectURL()Blob veya File için geçici bir blob: URL üretir

Blob: değiştirilemez ham veri

Blob (Binary Large Object), değiştirilemez ham veriyi temsil eden dosya benzeri bir nesnedir. new Blob(parcalar, secenekler) ile oluşturulur; parçalar dizisi string, ArrayBuffer, TypedArray, DataView veya başka Blob nesnelerinin karışımı olabilir. Stringler UTF-8 olarak kodlanır. type seçeneği MIME türünü belirler ve varsayılanı boş stringtir; endings seçeneği ise metindeki satır sonlarının olduğu gibi kopyalanmasını (transparent, varsayılan) veya işletim sisteminin kuralına çevrilmesini (native) belirler.

Özellikleri size (bayt cinsinden boyut) ve type'tır. İçeriği okumak için promise döndüren yöntemler vardır: text() UTF-8 metin, arrayBuffer() bir ArrayBuffer, bytes() bir Uint8Array verir; stream() ise içeriği parça parça okumak için bir ReadableStream döndürür. slice(baslangic, bitis, tur) asıl blob'u değiştirmeden bir alt aralık içeren yeni bir Blob üretir; negatif değerler sondan sayılır.

javascript
const blob = new Blob(["Merhaba, ", "dünya!"], { type: "text/plain" });
console.log(blob.size); // 16
console.log(blob.type); // text/plain
console.log(await blob.text()); // Merhaba, dünya!

const parca = blob.slice(0, 7, "text/plain");
console.log(await parca.text()); // Merhaba
console.log(blob.size); // 16

const bytes = new Uint8Array(await blob.slice(-3).arrayBuffer());
console.log(Array.from(bytes).join(",")); // 121,97,33
console.log(await new Response(blob).text()); // Merhaba, dünya!

Dikkat edin: 15 karakterlik metnin boyutu 16 bayttır, çünkü ü UTF-8'de iki bayt tutar. size her zaman bayt sayısıdır, karakter sayısı değil. slice(-3) son üç baytı (y, a, !) alır ve slice() sonrasında asıl blob'un boyutu değişmez. Son satır MDN'in gösterdiği alternatif okuma yoludur: bir Response nesnesine blob verip gövdesini okumak.

MDN'deki ilk örnek bir nesneyi JSON blob'una çevirir; bu, indirme bağlantısı hazırlamanın veya veriyi fetch() gövdesi olarak göndermenin temelidir. Kenar durumlarına da bakalım:

javascript
const veri = { tema: "koyu", dil: "tr" };
const blob = new Blob([JSON.stringify(veri, null, 2)], { type: "application/json" });
console.log(blob.type); // application/json
console.log(blob.size); // 35
const geri = JSON.parse(await blob.text());
console.log(geri.tema); // koyu

const bos = new Blob([]);
console.log(bos.size, JSON.stringify(bos.type)); // 0 ""

// Başlangıç boyuttan büyükse sonuç boş bir Blob olur
console.log(new Blob(["abc"]).slice(10).size); // 0

File: adı ve tarihi olan Blob

File, Blob'dan türeyen ve kullanıcının sistemindeki dosyalara ait bilgileri ekleyen bir arayüzdür. Bir Blob'un kullanılabildiği her yerde kullanılabilir: FileReader, URL.createObjectURL(), createImageBitmap(), fetch() gövdesi ve XMLHttpRequest.send(). Ek özellikleri name (yol bilgisi içermeyen dosya adı) ve lastModified'dır (Unix başlangıcından bu yana milisaniye). Dosyalar genellikle kullanıcıdan gelir ama new File(parcalar, ad, secenekler) ile kendiniz de oluşturabilirsiniz; lastModified verilmezse Date.now() kullanılır.

javascript
const dosya = new File(["ad,yas\nAyşe,30\n"], "liste.csv", {
  type: "text/csv",
  lastModified: 0,
});
console.log(dosya.name); // liste.csv
console.log(dosya.type); // text/csv
console.log(dosya.size); // 16
console.log(dosya.lastModified); // 0
console.log(dosya instanceof Blob); // true
const satirlar = (await dosya.text()).trim().split("\n");
console.log(satirlar.length); // 2
console.log(satirlar[1]); // Ayşe,30

type özelliği, tarayıcının belirleyebildiği MIME türüdür; belirlenemezse boş string olur. Bu değer dosya adının uzantısından türetilebildiği için güvenlik denetimi yerine geçmez: kullanıcı bir dosyanın uzantısını değiştirebilir. Sunucuya yüklenen her dosyayı sunucu tarafında yeniden doğrulayın.

Kullanıcının seçtiği dosyaları okumak

Kullanıcının seçtiği dosyaları okumak

En temel akış, dosya alanının change olayını dinlemek ve files listesini işlemektir. multiple özniteliği birden çok dosya seçmeye izin verir, accept ise seçim penceresinde gösterilecek türleri daraltır; bu özniteliklerin HTML tarafı HTML öğrenme rehberi kapsamındadır.

html
<input type="file" id="input" multiple accept="text/*" />
<pre id="cikti"></pre>
javascript
const input = document.getElementById("input");
const cikti = document.getElementById("cikti");

input.addEventListener("change", async () => {
  cikti.textContent = "";
  for (const file of input.files) {
    if (!file.type.startsWith("text")) continue;
    const metin = await file.text();
    cikti.textContent += `${file.name} (${file.size} bayt)\n${metin}\n\n`;
  }
});

Burada içerik textContent ile yazılıyor; dosyadan gelen metni innerHTML ile sayfaya basmak, içinde HTML olan bir dosyada betik çalıştırılmasına yol açabilir. Farkları innerHTML, textContent ve innerText yazısında anlatıyoruz.

MDN, çirkin görünen dosya alanını gizleyip kendi düğmenizle açmanın iki yolunu gösterir. Birincisi, alanı display: none ile gizleyip düğmenin click olayında fileElem.click() çağırmaktır. İkincisi JavaScript gerektirmez: alanla ilişkili bir <label> kullanırsınız. Bu durumda alanı display: none veya visibility: hidden ile gizlememelisiniz, çünkü etiket klavyeyle erişilemez hale gelir; görsel olarak gizleme (visually hidden) tekniği kullanılmalıdır.

Sürükle bırak ile dosya almak

Bir elemanı bırakma alanı yapmak için dragenter, dragover ve drop olaylarını dinlersiniz. İlk ikisinde varsayılan davranışı engellemezseniz tarayıcı bırakılan dosyayı kendisi açmaya çalışır. drop olayında dosyalar event.dataTransfer.files içindedir ve bu da bir FileList'tir; bu noktadan sonra işleme kodu dosya alanından gelen dosyalarla aynıdır. preventDefault() ve stopPropagation() ayrıntıları için olay yayılımı ve delegasyon yazısına bakın.

javascript
const dropbox = document.getElementById("dropbox");

function engelle(e) {
  e.stopPropagation();
  e.preventDefault();
}

dropbox.addEventListener("dragenter", engelle);
dropbox.addEventListener("dragover", engelle);
dropbox.addEventListener("drop", (e) => {
  engelle(e);
  dosyalariIsle(e.dataTransfer.files);
});

FileReader: olay tabanlı okuma

FileReader, Blob veya File içeriğini eş zamansız okur ve sonucu olaylarla bildirir. Okumayı dört yöntemden biriyle başlatırsınız: readAsText(blob, kodlama) metin verir (kodlama verilmezse UTF-8 varsayılır), readAsDataURL(blob) bir data: URL'si, readAsArrayBuffer(blob) bir ArrayBuffer verir. readAsBinaryString() da vardır, ama MDN bu yöntemin readAsArrayBuffer() lehine kullanımdan kaldırıldığını (deprecated) belirtir.

Sonuç, okuma bittikten sonra result özelliğinde bulunur. readyState üç değer alır: EMPTY (0, henüz veri yüklenmedi), LOADING (1, okuma sürüyor) ve DONE (2, tamamlandı). Hata olursa error özelliği bir DOMException taşır. Olaylar şunlardır: loadstart, okuma boyunca aralıklarla progress, başarıda load, hatada error, abort() çağrıldığında abort ve başarılı ya da başarısız her durumda en sonda loadend.

javascript
function dataUrlOku(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = () => reject(reader.error);
    reader.readAsDataURL(file);
  });
}

Günümüzde metin veya ikili veri için Blob yöntemleri (text(), arrayBuffer(), bytes()) daha kısa bir yol sunar. FileReader, ilerleme olaylarına ihtiyaç duyduğunuzda veya doğrudan bir data: URL'si istediğinizde hâlâ kullanışlıdır. MDN ayrıca FileReader artık kullanılmayacaksa olay dinleyicilerinin removeEventListener() ile kaldırılmasını, böylece bellek sızıntısının önlenmesini önerir.

Nesne URL'leri: createObjectURL ve revokeObjectURL

URL.createObjectURL(nesne), bir Blob, File veya MediaSource için içeriğe başvuran bir blob: URL stringi üretir. Bu adresi bir görselin src özniteliğine veya bir bağlantının href değerine verebilirsiniz. MDN'in vurguladığı iki kural vardır: her çağrı, aynı dosya için daha önce URL üretmiş olsanız bile yeni ve benzersiz bir URL oluşturur ve bunların her biri serbest bırakılmalıdır. Belge kapanınca otomatik serbest bırakılırlar, ama dinamik olarak çok sayıda URL üreten bir sayfada işiniz bittiğinde URL.revokeObjectURL() çağırmalısınız. Geçersiz veya zaten serbest bırakılmış bir URL ile revokeObjectURL() çağırmak hiçbir şey yapmaz.

javascript
function onizlemeleriGoster(dosyalar, liste) {
  liste.textContent = "";
  for (const file of dosyalar) {
    if (!file.type.startsWith("image/")) continue;
    const li = document.createElement("li");
    const img = document.createElement("img");
    img.src = URL.createObjectURL(file);
    img.height = 60;
    const bilgi = document.createElement("span");
    bilgi.textContent = `${file.name}: ${file.size} bayt`;
    li.append(img, bilgi);
    liste.append(li);
  }
}

function onizlemeyiKaldir(img) {
  URL.revokeObjectURL(img.src);
  img.closest("li").remove();
}

MDN örneğinde URL, görsel yüklendikten hemen sonra serbest bırakılmaz; çünkü bu, kullanıcının görsele sağ tıklayıp kaydetmesi veya yeni sekmede açması gibi etkileşimleri bozar. Uzun süre açık kalan uygulamalarda ise görsel DOM'dan kaldırıldığında URL'yi serbest bırakmak gerekir. Bu iki yöntem service worker içinde bulunmaz; MDN bunu bellek sızıntısı riskiyle açıklar. Eleman oluşturma ve kaldırma yöntemleri için DOM'da eleman oluşturma yazısına bakabilirsiniz.

readAsDataURL() ile nesne URL'si arasındaki fark şudur: veri URL'si dosyanın tüm içeriğini base64 olarak bir stringe gömer ve dosya büyüdükçe string de büyür; nesne URL'si ise içeriğe yalnızca kısa bir başvurudur ve tarayıcı, siz serbest bırakana veya belge kapanana kadar dosyaya olan başvuruyu tutar. MDN ayrıca bir uyarı ekler: readAsDataURL() sonucu doğrudan base64 olarak çözülemez, çünkü başında data:*/*;base64, bildirimi vardır; yalnızca base64 kısmını istiyorsanız önce bu öneki ayırmanız gerekir.

Mini örnek: büyük dosyayı parça parça okumak

Çok büyük bir dosyayı tek seferde text() veya arrayBuffer() ile okumak, tüm içeriği aynı anda belleğe alır. slice() yeni bir Blob döndürdüğü ve asıl blob'u değiştirmediği için dosyayı sabit boyutlu parçalara bölüp sırayla işleyebilirsiniz. Aşağıdaki işlev bir dosyadaki satır sonu baytlarını (10) sayar; örnekte etkiyi görmek için parça boyutu bilerek 4 bayt seçildi, gerçek kullanımda megabaytlar düzeyinde bir değer seçersiniz. Kod tarayıcıdan bağımsızdır ve Node.js ile çalıştırıldı:

javascript
async function satirSay(blob, parcaBoyutu = 4) {
  let satir = 0;
  let parcaSayisi = 0;
  for (let bas = 0; bas < blob.size; bas += parcaBoyutu) {
    const parca = new Uint8Array(await blob.slice(bas, bas + parcaBoyutu).arrayBuffer());
    parcaSayisi++;
    for (const b of parca) {
      if (b === 10) satir++;
    }
  }
  return { satir, parcaSayisi };
}

const dosya = new File(["bir\niki\nüç\n"], "sayilar.txt", { type: "text/plain" });
console.log(dosya.size); // 13
console.log(JSON.stringify(await satirSay(dosya))); // {"satir":3,"parcaSayisi":4}

Üç satırlık metin 13 bayttır, çünkü ü ve ç ikişer bayt tutar; 4 baytlık parçalarla dört okuma yapılır. Bayt düzeyinde bölmenin bir bedeli vardır: çok baytlı bir karakter iki parçanın sınırına denk gelebilir. Satır sonu gibi tek baytlık işaretleri saymak sorun değildir, ama parçaları ayrı ayrı metne çevirecekseniz bu sınırları hesaba katmanız gerekir. Metni parça parça işlemek için MDN'in listelediği blob.textStream() yöntemi, içeriği UTF-8 metin parçaları olarak veren bir ReadableStream döndürür; ham baytlar için blob.stream() vardır. Tarayıcıda aynı işlevi input.files[0] ile çağırırsınız.

Dosyayı sunucuya göndermek

File bir Blob olduğu için doğrudan fetch() gövdesi olarak gönderilebilir veya bir FormData nesnesine eklenebilir. Form ile gönderim FormData ve form gönderme, istek seçenekleri Fetch API yazısındadır. MDN'in yükleme örneği, yükleme ilerlemesini göstermek için XMLHttpRequest kullanır, çünkü MDN'e göre Fetch API bu ilerleme bildirimini desteklemez; xhr.upload üzerindeki progress olayı için XMLHttpRequest ve fetch farkı yazısına bakın.

javascript
async function yukle(file) {
  const form = new FormData();
  form.append("dosya", file, file.name);
  const yanit = await fetch("/api/yukle", { method: "POST", body: form });
  if (!yanit.ok) throw new Error(`Yükleme başarısız: ${yanit.status}`);
}

Sık yapılan hatalar

Sık yapılan hatalar: size değerini karakter sayısı sanmak, Nesne URL'lerini serbest bırakmamak, FileReader sonucunu hemen okumak, type değerine güvenmek
  • size değerini karakter sayısı sanmak: size bayt cinsindendir; Türkçe karakterler UTF-8'de birden fazla bayt tutar.
  • Nesne URL'lerini serbest bırakmamak: Her createObjectURL() çağrısı yeni bir URL üretir ve sayfa açık kaldıkça bellekte durur.
  • FileReader sonucunu hemen okumak: result yalnızca load veya loadend olayından sonra geçerlidir.
  • type değerine güvenmek: MIME türü belirlenemeyebilir veya uzantıyla yanıltılabilir; sunucuda doğrulayın.
  • Sürükle bırakta dragover olayını engellememek: Varsayılan davranış engellenmezse tarayıcı dosyayı kendisi açar.
  • readAsBinaryString kullanmak: Kullanımdan kaldırılmıştır; ikili veri için readAsArrayBuffer() veya blob.arrayBuffer() kullanın.

Sık Sorulan Sorular

JavaScript ile kullanıcının diskindeki bir dosyayı yol vererek okuyabilir miyim?

Hayır. File API yalnızca kullanıcının dosya alanıyla seçtiği veya sayfaya bıraktığı dosyalara erişir. MDN, yol adıyla okumak için kullanıcı onayına dayanan File System Access API'yi gösterir; tarayıcı desteği için MDN uyumluluk tablosuna bakın.

Blob ile File arasındaki fark nedir?

File, Blob'un alt türüdür ve ek olarak name ile lastModified taşır. Her File bir Blob'dur; bu yüzden Blob kabul eden her API File da kabul eder.

Büyük dosyaları belleği doldurmadan nasıl işlerim?

blob.slice() ile dosyayı parçalara bölüp sırayla okuyabilir veya blob.stream() ile bir ReadableStream alıp parça parça işleyebilirsiniz. Ağır işlemleri Web Worker içine taşımak arayüzün donmasını önler.

Seçilen dosyayı tarayıcıda saklayabilir miyim?

Evet. File ve Blob nesneleri yapılandırılmış klonlamayla kopyalanabildiği için IndexedDB içine doğrudan yazılabilir; localStorage ise yalnızca string sakladığı için uygun değildir.

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: Web API öğrenme rehberi · FormData ile form gönderme · IndexedDB · XMLHttpRequest ve fetch · Canvas ile görsel işlemleri · Storage API ve OPFS