Ders 24 / 25
Dosya Yükleme
Dosya alanının ürettiği değer, çok parçalı gönderim kodlamasının gövdedeki karşılığı, kabul türü bildirimi ve boyut sınırının nerede uygulandığı.
İçindekiler
Doğrulama, metin ve sayı alanları üzerinde kuruldu. Dosya alanı bu düzenlere uymaz. Değeri bir dizgi değildir; ikilik içerik taşır, kendi adı ve kendi içerik türü vardır.
Form Yapısı dersinde tanımlanan varsayılan kodlama —ad=deger çiftlerinin & ile
birleştirilmesi— bu içeriği taşıyamaz. Dosya alanı olan bir formda gönderim kodlaması
değiştirilir.
Üç Öznitelik
<form action="/olcum/yukle" method="post" enctype="multipart/form-data"> <p><label for="kayit">Ham kayıt dosyası</label> <input id="kayit" name="kayit" type="file" accept=".csv,text/csv" required></p> <p><button type="submit">Yükle</button></p> </form>
enctype="multipart/form-data" gönderim kodlamasını değiştirir. Yazılmadığında dosya
alanı yalnızca dosya adını gönderir; içerik gitmez. Bu, sessizce yanlış çalışan bir
formun en sık nedenidir: form gönderilir, sunucu bir değer alır, ama aldığı şey bir
dizgidir.
accept özniteliği, dosya seçicide hangi türlerin öne çıkarılacağını bildirir. Değeri
uzantı listesi, içerik türü listesi ya da ikisinin karışımı olabilir.
multiple özniteliği birden çok dosya seçilmesine izin verir; her dosya giriş listesine
aynı adla ayrı bir giriş olarak katılır.
Gövdenin Biçimi
Aşağıdaki betik bir sunucu açar, aynı iki alanı iki farklı kodlamayla gönderir ve
sunucunun aldığı ham gövdeyi yazdırır. Satır sonları görünür olsun diye \r\n dizisi
açıkça yazdırılmıştır.
// dosya.mjs — iki gonderim kodlamasinin govdedeki karsiligi import { createServer } from "node:http"; // Sinir dizgisi her gonderimde rastgele uretilir; ciktinin yinelenebilir olmasi icin // yazdirmadan once sabit bir yer tutucuyla degistirilir. const sunucu = createServer((istek, yanit) => { const parcalar = []; istek.on("data", (parca) => parcalar.push(parca)); istek.on("end", () => { const govde = Buffer.concat(parcalar); const tur = istek.headers["content-type"]; const sinir = tur.match(/boundary=(.+)$/)?.[1]; const maskele = (d) => (sinir ? d.split(sinir).join("SINIR") : d); console.log("content-type :", maskele(tur)); console.log("govde uzunlugu:", govde.length, "bayt"); console.log("--- ham govde ---"); console.log(maskele(govde.toString()).replace(/\r\n/g, "\\r\\n\n")); console.log("==="); yanit.writeHead(204).end(); }); }); sunucu.listen(0, "127.0.0.1", async () => { const adres = "http://127.0.0.1:" + sunucu.address().port + "/yukle"; // enctype="multipart/form-data" const veri = new FormData(); veri.append("istasyon", "kuzey-yamac"); veri.append("kayit", new Blob(["saat,sicaklik\n07:00,-4.2\n"], { type: "text/csv" }), "olcum.csv"); await fetch(adres, { method: "POST", body: veri }); // enctype="application/x-www-form-urlencoded" — ayni iki alan, dosya icerigi olmadan await fetch(adres, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: new URLSearchParams([["istasyon", "kuzey-yamac"], ["kayit", "olcum.csv"]]).toString(), }); sunucu.close(); });
content-type : multipart/form-data; boundary=SINIR govde uzunlugu: 295 bayt --- ham govde --- --SINIR\r\n Content-Disposition: form-data; name="istasyon"\r\n \r\n kuzey-yamac\r\n --SINIR\r\n Content-Disposition: form-data; name="kayit"; filename="olcum.csv"\r\n Content-Type: text/csv\r\n \r\n saat,sicaklik 07:00,-4.2 \r\n --SINIR--\r\n === content-type : application/x-www-form-urlencoded govde uzunlugu: 36 bayt --- ham govde --- istasyon=kuzey-yamac&kayit=olcum.csv ===
Çıktıdaki SINIR bir yer tutucudur: gerçek sınır dizgisi her gönderimde rastgele
üretilir ve betik onu yazdırmadan önce sabit bir metinle değiştirir. Sınırın metni
gönderen tarafça seçilir; tek koşulu, gövdenin hiçbir yerinde geçmemesidir. Gövde
uzunluğu bu betikte değişmez, çünkü üretilen sınırın uzunluğu sabittir.
Gövde yapısı şöyledir. Content-Type başlığında bir sınır (boundary) dizgisi
bildirilir. Gövde bu sınırla parçalara ayrılır; her parça kendi başlıklarını, bir boş
satırı ve içeriğini taşır. Content-Disposition başlığı alanın adını verir; dosya
alanlarında ayrıca filename parametresi bulunur. Dosya parçasında bir de Content-Type
başlığı vardır ve dosyanın türünü bildirir. Gövde, sınırın sonuna iki tire eklenmiş
biçimiyle biter.
İki temel fark görünür.
İçerik kaçırılmaz. Çok parçalı kodlamada dosya içeriği olduğu gibi taşınır; yüzde kodlaması uygulanmaz. Ayırma işini sınır dizgisi yapar. İkilik bir dosya, kodlanmadan gönderilebilir.
Yük oranı yüksektir. Aynı iki alan için 36 bayta karşılık 295 bayt kullanılmıştır. Her parça kendi başlıklarını taşır ve sınır dizgisi her parçada yinelenir. Bu maliyet dosya büyüdükçe oransal olarak küçülür; ancak dosya alanı olmayan bir formu çok parçalı kodlamayla göndermek gereksiz bir yüktür.
Çıktının ikinci bölümü, yanlış kodlamanın sonucunu gösterir: kayit alanı yalnızca dosya
adını taşır. Sunucu bir değer alır ve içerik yoktur.
Kabul Türü Bir Kısıt Değildir
accept özniteliği dosya seçici penceresini süzer. Süzgeç kullanıcı tarafından
kaldırılabilir ve gönderim belge kullanılmadan da yapılabilir; Yerleşik Doğrulama dersindeki
sonuç burada da geçerlidir.
Dosyanın gerçekten bildirdiği türde olup olmadığı, gövdedeki Content-Type başlığından
anlaşılmaz: o başlığı da gönderen taraf yazar. Sunucu türü ancak dosyanın içeriğine
bakarak doğrulayabilir — dosya biçimlerinin baş baytlarındaki tanımlayıcı diziler bu iş
içindir.
Uzantıya güvenmek de yeterli değildir. Yüklenen dosya adı sunucuda doğrudan kullanılmaz: kullanıcıdan gelen ad yol ayıracı içerebilir, var olan bir dosyanın üzerine yazabilir ya da sunucunun çalıştırdığı bir uzantı taşıyabilir. Güvenli davranış, sunucunun kendi ürettiği bir ad kullanması ve özgün adı yalnızca üstveri olarak saklamasıdır.
Alanın Değeri Belgeden Yazılamaz
Dosya alanı, diğer alanlardan bir yönüyle daha ayrılır: değeri belgeden verilemez.
value özniteliği yazılsa da yok sayılır, çünkü aksi hâlde bir belge, kullanıcının
diskindeki herhangi bir dosyayı sessizce gönderebilirdi. Alanın değeri yalnızca
kullanıcının seçimiyle oluşur.
Aynı gerekçe dosyanın yolunu da gizler. Belgeye açılan bilgi dosyanın adı, boyutu ve türüdür; bulunduğu dizin değildir. Kullanıcının dizin yapısı, o kullanıcı hakkında bilgi taşır ve belgeye verilmez.
Bunun bir sonucu, bir formun önceki gönderimden kalan dosyayı yeniden göstermesinin olanaksız olmasıdır. Doğrulama hatası nedeniyle geri dönülen bir formda diğer alanların değerleri belgede yeniden yazılabilir; dosya alanı boş kalır ve kullanıcının dosyayı yeniden seçmesi gerekir. Çok adımlı formlarda bu, dosyanın erken bir adımda yüklenip sunucuda geçici olarak saklanmasını ve sonraki adımlarda bir kimlikle anılmasını gerektirir.
Boyut Sınırı Belgede Bildirilemez
Dosya boyutunu sınırlayan bir öznitelik yoktur. Bunun bir eksiklik değil, tutarlı bir tasarım olduğu söylenebilir: belgede bildirilen bir sınır zaten uygulanamazdı.
Sınır iki yerde uygulanır. Sunucu, gövdenin tamamını okumadan Content-Length başlığına
bakarak isteği reddedebilir ve okurken de bir üst sınır uygulayabilir; bu, belleğin bir
istekle doldurulmasını önler. Sunucu yazılımının kendi yapılandırması da genellikle bir
istek gövdesi üst sınırı taşır.
Kullanıcıya erken geri bildirim vermek isteniyorsa dosya boyutu gönderim öncesinde davranış katmanında okunabilir; bu bir kolaylıktır, kısıt değildir. Beklenen sınır belgede metin olarak yazılır — kullanıcı, seçtiği dosyanın reddedileceğini yükleme bittikten sonra öğrenmemelidir.
İstasyon Belgesinde
<fieldset> <legend>Ham kayıt</legend> <p><label for="kayit">Ölçüm dosyası</label> <input id="kayit" name="kayit" type="file" accept=".csv,text/csv" aria-describedby="kayit-yardim"> <span id="kayit-yardim">Virgülle ayrılmış değer dosyası, en çok 5 MB.</span></p> </fieldset>
Formun enctype özniteliği bu alan eklendiği anda değiştirilmelidir; unutulduğunda form
çalışıyor görünür ve dosya içeriği hiç ulaşmaz.
Özet
- Dosya alanı olan formda
enctype="multipart/form-data"yazılır; yazılmadığında yalnızca dosya adı gönderilir ve içerik kaybolur. - Çok parçalı gövde bir sınır dizgisiyle parçalara ayrılır; her parça kendi başlıklarını taşır ve içerik kaçırılmadan aktarılır.
- Bu kodlamanın yük oranı yüksektir; dosya alanı olmayan formlarda kullanılmaz.
acceptyalnızca dosya seçiciyi süzer; tür denetimi sunucuda, dosyanın içeriğine bakılarak yapılır ve yüklenen ad doğrudan kullanılmaz.- Boyut sınırı belgede bildirilemez; sunucuda uygulanır ve kullanıcıya metinle bildirilir.
Sonraki Adım
Form bu noktada veri topluyor, doğruluyor ve dosya alabiliyor. Geriye bir soru kalıyor: gönderim reddedildiğinde kullanıcı ne görüyor? Tarayıcının kendi iletisi tek alanı gösteriyor, sunucudan dönen hata ise yeni bir belgeyle geliyor. Son ders hata bildirimini ve odak yönetimini ele alır ve kursu, bu formun erişilebilir sürümünü kurarak kapatır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.