Mundarija (32)
- Bu darsda
- 1. Nega bu kerak?
- 2. Promise bilan o'qish: zamonaviy usullar
- 2.1 To'rtta usul
- 2.2 await ni unutish
- 2.3 Katta fayl: stream()
- 3. FileReader: eski, hodisali usul
- 3.1 Nega uni bilish kerak
- 3.2 Hodisalar tartibi
- 3.3 readAsDataURL: fayl — manzil ichida
- 4. Tekshiruv: hajm, tur va sehrli baytlar
- 4.1 Uch daraja
- 4.2 Birinchi baytlarni o'qish
- 5. «Bahor»: chek rasmini oldindan ko'rish
- 5.1 Hammasi birga
- 5.2 Kod qanday ishlaydi
- 5.3 Nega accept yetmaydi
- 6. Hujumchi nigohi
- 7. Ko'p uchraydigan xatolar
- 7.1 await siz o'qish
- 7.2 FileReader.result ni darhol o'qish
- 7.3 Ko'rsatish uchun data URL
- 7.4 Katta faylni text() bilan o'qish
- 7.5 file.type ga ishonish
- 8. Mashqlar
- 1-mashq (oson): So'zlarni sanash
- 2-mashq (o'rta): PDF tekshiruvi
- 3-mashq (qiyin): Avatarni saqlash
- 4-mashq: Vazifalar qadami — import fayldan
- 9. Real ishda
- Xulosa
- Manbalar
JavaScript'da faylni o'qish: text(), arrayBuffer(), FileReader va rasm preview
Qisqacha: Fayl (
FileyokiBlob) ichini o'qishning zamonaviy usullari Promise qaytaradi:await file.text()— matn,await file.arrayBuffer()yokifile.bytes()— baytlar,file.stream()— katta faylni bo'laklab. EskiFileReaderesa hodisalar bilan ishlaydi vareadAsDataURLbilan faylnidata:manzilga aylantiradi. Rasmni oldindan ko'rsatish uchun obyekt URL yetadi. Yuklashdan oldin hajmni (size), turni va birinchi baytlarni tekshirib, foydalanuvchiga darhol javob berasiz — lekin bu himoya emas, qulaylik.
Bu darsda
- Faylni matn,
ArrayBuffer,Uint8Arrayva oqim (stream) sifatida o'qiy olasiz. FileReaderhodisalarini (load,error,progress) vareadAsDataURLni tushuntira olasiz.- Tanlangan rasmni yuklashdan oldin ko'rsatasiz va uning o'lchamini bilib olasiz.
- Faylni hajm, tur va sehrli baytlar bo'yicha tekshirasiz.
vazifalarga fayldan import qilishni qo'shasiz.
Oldin bilishingiz kerak: File va Blob, Typed arrays va binar ma'lumot, Streams API, async / await.
1. Nega bu kerak?
Fayl yuklash maydoni darsida «Bahor» bron formasiga to'lov cheki maydonini qo'shgan edik. O'shanda bir muammoni ochiq qoldirdik: HTML fayl hajmini cheklay olmaydi. Mehmon telefonidan 12 MB lik suratni tanlaydi, sekin internetda 3 daqiqa yuboradi va oxirida "fayl juda katta" degan xabarni oladi. "Buni JavaScript bilan, fayl tanlangan zahoti tekshiramiz" degan edik.
Yana ikki holat:
- Mehmon chek o'rniga boshqa rasmni — masalan, osh suratini — tanlab qo'ydi. Ko'rsa, darhol tushunardi. Lekin u faqat fayl nomini ko'radi:
IMG_2041.jpg. - Kimdir
.txtfaylnichek.pngdeb qayta nomlab yubordi. Kengaytma "rasm" deydi, ichida esa matn.
O'tgan darsda faylni yopiq quti sifatida ishlatdik: nomini, hajmini o'qidik, unga manzil berdik. Ichiga qaramadik. Bugun qutini ochamiz: matnni o'qiymiz, baytlarga qaraymiz, rasmni ekranga chiqaramiz. Hammasi fayl serverga ketishidan oldin.
2. Promise bilan o'qish: zamonaviy usullar
2.1 To'rtta usul
Blob (demak, File ham) ichini o'qishning to'rtta usuli bor:
| Usul | Nima qaytaradi | Qachon |
|---|---|---|
text() |
Promise<string> |
matnli fayl: .txt, .csv, .json |
arrayBuffer() |
Promise<ArrayBuffer> |
baytlar bilan ishlash (eski API'lar shuni kutadi) |
bytes() |
Promise<Uint8Array> |
baytlar, darhol o'qishga tayyor |
stream() |
ReadableStream |
katta fayl, bo'laklab |
Birinchi uchtasi butun faylni xotiraga o'qiydi va Promise qaytaradi. stream() esa darhol oqim qaytaradi — o'qish siz bo'lak so'raganda bo'ladi.
Node 24 da ham shular bor. «Bahor» menyusining kichik CSV fayli:
const menu = new File(["Osh;35000\nManti;30000\n"], "menyu.csv", {
type: "text/csv",
});
const text = await menu.text();
console.log(text.split("\n")[0]); // Osh;35000
const buffer = await menu.arrayBuffer();
console.log(buffer.byteLength); // 22
const bytes = await menu.bytes();
console.log(bytes.slice(0, 5));
// Uint8Array(5) [ 79, 115, 104, 59, 51 ]Qatorma-qator:
text()— baytlarni UTF-8 deb o'qib, satr qaytaradi. Boshqa kodlashdagi fayl (masalan, eski Windows-1251) buziladi — bunday holdaTextDecoder("windows-1251")kerak.arrayBuffer()— xom baytlar "xotira bo'lagi". Typed arrays darsini eslang:ArrayBufferni to'g'ridan-to'g'ri o'qib bo'lmaydi, uniUint8Arrayga o'rash kerak.bytes()— xuddi shu baytlar, lekin allaqachonUint8Arrayichida.79, 115, 104, 59, 51—O,s,h,;,3belgilarining kodlari. Bu usul yangi: hamma asosiy brauzerlarda 2025-yil boshidan beri bor. Eski brauzer kerak bo'lsa —new Uint8Array(await blob.arrayBuffer()).
2.2 await ni unutish
Bu usullar Promise qaytaradi. await siz:
const menu = new File(["Osh;35000"], "menyu.csv");
console.log(menu.text()); // Promise { <pending> }Promise { <pending> } — "hali bajarilmagan va'da". Fayl o'qilishi vaqt oladi (disk sekin bo'lishi mumkin), shuning uchun natija darhol emas. Chrome konsoli buni Promise {<pending>} deb ko'rsatadi — ma'nosi bir xil. Tuzatish: await menu.text() (async / await).
2.3 Katta fayl: stream()
Jasur aka bir yillik bronlar faylini yuklamoqchi — 50 MB lik CSV. text() uni butunlay xotiraga oladi va yana bir nusxasini satr qilib yaratadi. Telefonda bu sekin, ba'zan sahifa qulab tushadi.
stream() faylni bo'laklab beradi. Streams API darsida fetch javobini xuddi shunday o'qigan edik — o'sha ReadableStream, o'sha TextDecoderStream. Qatorlarni sanaymiz:
const rows = Array.from(
{ length: 10_000 },
(_, i) => `Bron ${i + 1};19:00;4`,
);
const file = new Blob([rows.join("\n")]);
let lineCount = 0;
let tail = "";
const stream = file.stream().pipeThrough(new TextDecoderStream());
for await (const chunk of stream) {
const parts = (tail + chunk).split("\n");
tail = parts.pop(); // oxirgi qator to'liq kelmagan bo'lishi mumkin
lineCount += parts.length;
}
if (tail !== "") lineCount++;
console.log(file.size); // 178893
console.log(lineCount); // 10000Qatorma-qator:
file.stream()— oqim.pipeThrough(new TextDecoderStream())— baytlarni matnga aylantirib beradigan "quvur" (Streams API darsidagi kabi). U bo'lak chegarasida bo'lingan ko'p baytli harfni ham to'g'ri yig'adi.for await— har bo'lak kelganda bir aylanish (Asinxron iteratsiya).tail— eng muhim joy. Bo'lak chegarasi qatorning o'rtasiga tushishi mumkin:Bron 34bir bo'lakda,56;19:00;4keyingisida. Oxirgi chala qatorni keyingi bo'lakka qoldiramiz.
Bo'laklar soni muhitga bog'liq. Node 24 da xotirada yaratilgan bu Blob bitta bo'lakda keldi. Chrome 154 da esa diskdan tanlangan 1,2 MB lik fayl 6 bo'lakda keldi, eng kattasi 384 KB. Kodingiz bo'lak hajmiga tayanmasin. Xotirada esa bir vaqtda faqat bitta bo'lak turadi.
Tekshirib ko'ring: Nega stream misolida
tailishlatildi? Har bo'lakni shunchakichunk.split("\n").lengthbilan sanasak nima bo'lardi?
Javob
Bo'lak chegarasi qator o'rtasiga tushganda, o'sha qator ikki bo'lakda yarim-yarim keladi. U ikki marta sanaladi. 6 bo'lakli faylda 5 ta chegara bor — natija 10 005 kabi noto'g'ri son bo'lardi. tail chala qatorni keyingi bo'lak boshiga ulaydi.
3. FileReader: eski, hodisali usul
3.1 Nega uni bilish kerak
text(), arrayBuffer() va stream() Blob'ga 2019–2020-yillarda qo'shilgan. Undan oldin faylni o'qishning yagona yo'li — FileReader edi. U Promise emas, hodisalar bilan ishlaydi. Uni bilish uchun ikki sabab bor:
- Eski kod va kutubxonalarda, internetdagi ko'p maqolalarda aynan shu.
readAsDataURL— faylnidata:manzilga aylantirish. Uning Blob'da Promise'li juftlari yo'q.
3.2 Hodisalar tartibi
FileReader bilan ish uch qadam: obyekt yaratish → hodisalarga tinglovchi → o'qishni boshlash:
<style>
body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
pre { white-space: pre-wrap; background: #f3f6f3;
padding: 0.5rem; max-height: 8rem; overflow: auto; }
</style>
<label for="file">Matnli fayl tanlang (.txt, .csv):</label>
<input type="file" id="file" accept=".txt,.csv,text/plain">
<pre id="preview"></pre>
<script>
const input = document.getElementById("file");
const preview = document.getElementById("preview");
input.addEventListener("change", () => {
const [file] = input.files;
if (file === undefined) return;
const reader = new FileReader();
for (const type of ["loadstart", "progress", "load", "loadend"]) {
reader.addEventListener(type, (event) => {
console.log(`${type}: ${event.loaded} / ${event.total} bayt`);
});
}
reader.addEventListener("load", () => {
preview.textContent = reader.result.slice(0, 300);
});
reader.addEventListener("error", () => {
console.log(`Xato: ${reader.error.name}`);
});
reader.readAsText(file);
console.log(`Darhol: result = ${reader.result}`);
});
</script>Biz 11 baytli a.txt faylini tanladik — konsolga shu qatorlar chiqdi:
Darhol: result = null
loadstart: 0 / 11 bayt
progress: 11 / 11 bayt
load: 11 / 11 bayt
loadend: 11 / 11 baytQatorma-qator:
reader.readAsText(file)— o'qishni boshlaydi, lekin natijani qaytarmaydi. Shuning uchun keyingi qatordagireader.resulthalinull. Bu eng ko'p uchraydigan xato: natija faqatloadhodisasida tayyor.loadstart→progress(bir yoki bir necha marta) →load(muvaffaqiyat) yokierror→loadend(har holda oxirida).event.loaded/event.total— o'qilgan va jami baytlar. Katta fayldaprogressbir necha marta keladi — progress chizig'i uchun. Sinovda 34 MB lik faylda ham atigi ikki marta keldi: disk tez.reader.result— natija:readAsTextda satr,readAsArrayBufferdaArrayBuffer,readAsDataURLdadata:satr.- Matnni
textContentbilan chiqaramiz — fayl ichida HTML bo'lsa ham, u faqat matn bo'lib ko'rinadi.
Bitta FileReader bir vaqtda bitta faylni o'qiydi. Ikkinchi fayl uchun — yangi FileReader.
Maslahat: Yangi kodda
FileReaderni Promise'ga o'rash shart emas —file.text()aynan shuning o'zi.FileReaderfaqatreadAsDataURLuchun yokiprogresskerak bo'lganda.
3.3 readAsDataURL: fayl — manzil ichida
Oldingi darsda obyekt URL va data URL'ni taqqosladik. Data URL'da butun fayl manzilning o'zida, base64 ko'rinishida. Base64 — baytlarni 64 ta "xavfsiz" belgi (lotin harflari, raqamlar, +, /) bilan yozish usuli. Har 3 bayt — 4 belgi.
osh.png (5 747 bayt) uchun readAsDataURL natijasining boshi:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAIAAAC6s0uzAAAAUzunligi — 7 686 belgi. Fayldan qariyb uchdan bir katta. Katta rasmlarda farq yana ham sezilarli:
- 1 MB rasm1 000 KB
- data URL+33%1 333 KB
- 5 MB rasm5 000 KB
- data URL+33%6 667 KB
Manba: Hisob: base64 har 3 baytni 4 belgi qiladi (4 × n ÷ 3); Node 24 Buffer.toString("base64") bilan tekshirildi
Nimaga qarang: 5 MB rasm data URL'da 6,7 MB satrga aylanadi. Bu satr xotirada turadi, <img src> ga yozilganda brauzer uni yana qayta ochadi. Shuning uchun ko'rsatish uchun data URL emas, obyekt URL — u nusxasiz va bir zumda. Data URL faqat satr kerak bo'lganda: localStorage ga saqlash, JSON ichida yuborish, <canvas> bilan ishlash.
Tekshirib ko'ring:
reader.readAsText(file); preview.textContent = reader.result;— bu kod nima ko'rsatadi?
Javob
Hech narsa (null bo'sh satr bo'lib ko'rinadi). readAsText faqat o'qishni boshlaydi, natija keyinroq, load hodisasida keladi. To'g'ri yozuv — reader.addEventListener("load", () => { preview.textContent = reader.result; }), yoki zamonaviy usul: preview.textContent = await file.text().
4. Tekshiruv: hajm, tur va sehrli baytlar
4.1 Uch daraja
Fayl tanlangan zahoti uch narsani tekshirish mumkin. Har biri oldingisidan ishonchliroq:
- Hajm —
file.size. Aniq, chunki brauzer uni diskdan oladi. - Tur —
file.typeyoki kengaytma. Oldingi darsda ko'rdik: brauzer uni nomdan taxmin qiladi. Fayl qayta nomlansa — tur ham "o'zgaradi". - Sehrli baytlar (magic bytes) — faylning birinchi baytlari. Fayl yuklash maydoni darsidagi "Hujumchi nigohi" bo'limida ularni tilga olgan edik: har format o'ziga xos baytlar bilan boshlanadi.
| Format | Birinchi baytlar (hex) | Belgilarda |
|---|---|---|
| PNG | 89 50 4E 47 0D 0A 1A 0A |
.PNG.... |
| JPEG | FF D8 FF |
— |
| GIF | 47 49 46 38 |
GIF8 |
25 50 44 46 |
%PDF |
WebP biroz murakkab: 52 49 46 46 (RIFF), keyin 4 bayt hajm, keyin 57 45 42 50 (WEBP).
4.2 Birinchi baytlarni o'qish
Butun faylni o'qish shart emas. O'tgan darsdagi slice() bilan faqat boshini kesamiz — u nusxasiz va bir zumda — va faqat shu 12 baytni o'qiymiz:
const SIGNATURES = [
{ type: "image/png", bytes: [0x89, 0x50, 0x4e, 0x47] },
{ type: "image/jpeg", bytes: [0xff, 0xd8, 0xff] },
{ type: "image/gif", bytes: [0x47, 0x49, 0x46, 0x38] },
];
async function detectImageType(file) {
const head = await file.slice(0, 12).bytes();
for (const { type, bytes } of SIGNATURES) {
if (bytes.every((byte, i) => head[i] === byte)) return type;
}
const ascii = String.fromCharCode(...head);
if (ascii.startsWith("RIFF") && ascii.slice(8) === "WEBP") {
return "image/webp";
}
return null;
}
const png = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a]);
const real = new File([png], "osh.png", { type: "image/png" });
const fake = new File(["Bu rasm emas"], "chek.png", {
type: "image/png",
});
console.log(await detectImageType(real)); // image/png
console.log(await detectImageType(fake)); // null
console.log(fake.type); // image/pngQatorma-qator:
SIGNATURES— har format uchun "imzo": tur nomi va boshidagi baytlar ro'yxati.file.slice(0, 12).bytes()— faylning faqat birinchi 12 bayti,Uint8Arraybo'lib.bytes.every((byte, i) => head[i] === byte)— imzoning har bayti fayl boshidagi o'sha o'rindagi baytga tengmi? Bittasi farq qilsa —false, keyingi imzoga o'tamiz.0x89— o'n oltilik yozuv:0x89= 137. Jadvaldagi baytlarni shu ko'rinishda yozish qulay.String.fromCharCode(...head)— baytlarni harflarga aylantiradi: WebP'ningRIFFvaWEBPbelgilarini satr sifatida solishtirish qulayroq.
fake — oddiy matn, lekin nomi chek.png va type "image/png". Kengaytma ham, tur ham aldadi. Birinchi baytlar esa 42 75 20 72 — Bu r harflari. detectImageType uni rad etdi.
Diqqat: Sehrli baytlar ham to'liq himoya emas. Hujumchi faylning boshiga PNG baytlarini qo'yib, keyin istalgan narsani yozishi mumkin. Brauzerdagi tekshiruv — halol foydalanuvchiga tez javob berish uchun. Haqiqiy tekshiruv baribir serverda (Fayl yuklash xavfsizligi, backend qismida).
5. «Bahor»: chek rasmini oldindan ko'rish
5.1 Hammasi birga
Endi bron formasidagi chek maydoni. Qoidalar: PNG, JPEG yoki WebP; 5 MB gacha; rasm haqiqatan rasm bo'lsin. Tanlangan zahoti — tekshiruv, keyin oldindan ko'rish va o'lchami:
<style>
body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
#preview { display: block; max-width: 100%; max-height: 10rem;
margin-top: 0.5rem; }
#error { color: #b3261e; min-height: 1.5em; margin: 0.25rem 0; }
</style>
<label for="receipt">
To'lov cheki (PNG, JPG yoki WebP, 5 MB gacha)
</label>
<input type="file" id="receipt"
accept="image/png,image/jpeg,image/webp" aria-describedby="error">
<p id="error" aria-live="polite"></p>
<p id="details"></p>
<script>
const MAX_SIZE = 5 * 1024 * 1024;
const input = document.getElementById("receipt");
const error = document.getElementById("error");
const details = document.getElementById("details");
// rasm tayyor bo'lgandagina sahifaga qo'shiladi
const preview = document.createElement("img");
preview.id = "preview";
let previewUrl = null;
async function detectImageType(file) {
const head = await file.slice(0, 12).bytes();
const hex = [...head].map((b) => b.toString(16).padStart(2, "0"));
const start = hex.join(" ");
if (start.startsWith("89 50 4e 47")) return "image/png";
if (start.startsWith("ff d8 ff")) return "image/jpeg";
const ascii = String.fromCharCode(...head);
if (ascii.startsWith("RIFF") && ascii.slice(8) === "WEBP") {
return "image/webp";
}
return null;
}
function reset(message) {
error.textContent = message;
input.setAttribute("aria-invalid", message ? "true" : "false");
preview.remove();
details.textContent = "";
if (previewUrl !== null) URL.revokeObjectURL(previewUrl);
previewUrl = null;
}
input.addEventListener("change", async () => {
const [file] = input.files;
reset("");
if (file === undefined) return;
if (file.size > MAX_SIZE) {
const mb = (file.size / 1024 / 1024).toFixed(1);
reset(`Fayl juda katta: ${mb} MB. Ko'pi bilan 5 MB.`);
console.log(`Rad: ${file.name} — hajm`);
return;
}
const realType = await detectImageType(file);
if (realType === null) {
reset("Bu fayl rasm emas. PNG, JPG yoki WebP tanlang.");
console.log(`Rad: ${file.name} — sehrli baytlar`);
return;
}
previewUrl = URL.createObjectURL(file);
preview.src = previewUrl;
preview.alt = `Tanlangan chek: ${file.name}`;
await preview.decode();
details.before(preview);
const kb = (file.size / 1024).toFixed(1);
details.textContent = `${preview.naturalWidth} × `
+ `${preview.naturalHeight} px, ${kb} KB, ${realType}`;
console.log(`Qabul: ${file.name} — ${details.textContent}`);
});
</script>Biz to'rtta faylni ketma-ket tanladik (Chrome 154). Konsolda har biri uchun bitta qator:
Qabul: osh.png — 640 × 480 px, 5.6 KB, image/png
Qabul: manti.jpg — 300 × 200 px, 0.6 KB, image/jpeg
Rad: soxta.png — sehrli baytlar
Rad: katta.png — hajmsoxta.png — nomi rasm, ichida Bu rasm emas degan matn. katta.png — 34,4 MB lik haqiqiy rasm (xato matnida Fayl juda katta: 34.4 MB). WebP rasm bilan ham sinadik: Qabul: lagmon.webp — 200 × 100 px, 0.1 KB, image/webp.
5.2 Kod qanday ishlaydi
Tartib muhim — arzon tekshiruvdan qimmatiga:
- Hajm — hech narsa o'qilmaydi, bir zumda. 34 MB lik fayl shu yerda to'xtaydi va diskdan bir bayt ham o'qilmaydi.
- Sehrli baytlar — faqat 12 bayt o'qiladi.
- Ko'rsatish —
URL.createObjectURL(nusxasiz) vapreview.decode().
Bu misoldagi detectImageType avvalgisidan biroz boshqacha yozilgan. b.toString(16).padStart(2, "0") har baytni ikki belgili o'n oltilik satrga aylantiradi: 137 → "89", 5 → "05". Keyin ular bo'sh joy bilan qo'shiladi — "89 50 4e 47 …" — va jadvaldagi yozuv bilan startsWith orqali solishtiriladi. Natija bir xil, faqat o'qish osonroq.
img.decode() — Promise: rasm to'liq ochilib (decode), chizishga tayyor bo'lganda bajariladi. Shundan keyin naturalWidth va naturalHeight — rasmning haqiqiy o'lchami. Rasm ochilmasa, decode() rad etiladi. Chrome 154 da buzuq fayl uchun xabar: EncodingError: The source image cannot be decoded. Bizning kodda bu holat sehrli baytlar tekshiruvi tufayli kamdan-kam yetib keladi.
<img> ni JavaScript yaratadi va faqat rasm tayyor bo'lganda sahifaga qo'shadi: src siz bo'sh rasm sahifada turmasin. reset() har yangi tanlovda eski xatoni, eski rasmni va eski obyekt URL'ni tozalaydi. Xato matni aria-live="polite" maydonida — u o'zgarganda ekran o'quvchi uni o'qib beradi. Malika (NVDA bilan) "Fayl juda katta" ni eshitadi. aria-invalid — maydonning o'zi ham "xato" deb belgilanadi (Dinamik UI'da qulaylik).
Maslahat: Rasm o'lchami ham tekshirilishi mumkin: masalan, "kamida 600 px kenglik". Buning uchun
naturalWidthyetadi.decode()o'rnigacreateImageBitmap(file)ham ishlaydi — u<img>siz ochadi vawidth/heightberadi.
5.3 Nega accept yetmaydi
accept="image/png,image/jpeg,image/webp" fayl tanlash oynasida boshqa fayllarni yashiradi. Lekin foydalanuvchi oynada "Barcha fayllar" ni tanlashi yoki faylni sudrab tashlashi mumkin. accept — maslahat, tekshiruv emas. Shuning uchun kodda ham tekshiramiz.
Tekshirib ko'ring: Nega kodda hajm tekshiruvi sehrli baytlardan oldin turadi? Teskari tartibda nima yo'qotamiz?
Javob
Hajm file.size dan bir zumda olinadi — diskdan hech narsa o'qilmaydi. Sehrli baytlar uchun esa diskka murojaat qilib, 12 bayt o'qish va await bilan kutish kerak. Teskari tartibda 34 MB lik fayl uchun ham avval diskka borilardi, keyin baribir hajm sabab rad etilardi. Natija bir xil, lekin ortiqcha ish. Qoida: arzon tekshiruv — birinchi.
6. Hujumchi nigohi
Brauzerdagi tekshiruvni chetlab o'tish oson: hujumchi DevTools'da kodingizni o'chiradi yoki so'rovni brauzersiz, curl bilan yuboradi. Shuning uchun bu darsdagi hamma tekshiruvning maqsadi — halol foydalanuvchiga tez javob. Uch qoida:
- Server hammasini qayta tekshiradi. Hajm chegarasi (Nginx 413), sehrli baytlar, tasodifiy nom — Fayl yuklash maydoni darsidagi ro'yxat.
- Fayl tarkibini HTML deb chiqarmang. Matnli faylni ko'rsatganda —
textContent,innerHTMLemas..txtichida<script>bo'lishi mumkin. - Rasmni faqat
<img>da ko'rsating. SVG — matnli format va unda skript bo'lishi mumkin.<img src="blob:…">ichida skriptlar ishlamaydi. O'sha SVG'niinnerHTMLbilan qo'ysangiz yoki obyekt URL'ini yangi tabda ochsangiz — ishlaydi. Shuning uchun chek maydonida SVG umuman qabul qilinmaydi.
7. Ko'p uchraydigan xatolar
7.1 await siz o'qish
file.text() → Promise { <pending> }. Tuzatish: await file.text().
7.2 FileReader.result ni darhol o'qish
readAsText dan keyingi qatorda result — null. Tuzatish: load hodisasini kuting.
7.3 Ko'rsatish uchun data URL
5 MB rasm — 6,7 MB satr, sekin va xotira yeydi. Tuzatish: URL.createObjectURL(file) + keyin revokeObjectURL.
7.4 Katta faylni text() bilan o'qish
50 MB CSV — butunlay xotirada, ikki nusxada. Tuzatish: stream() + TextDecoderStream, bo'laklab.
7.5 file.type ga ishonish
Tur nomdan olinadi. Tuzatish: hajm + sehrli baytlar, va baribir server tekshiruvi.
8. Mashqlar
1-mashq (oson): So'zlarni sanash
Node'da new File() bilan "sharh.txt" yarating, tarkibi — "Osh juda mazali edi. Rahmat, Bahor!". await file.text() bilan o'qib, so'zlar sonini chiqaring.
Ishora: text.split(" ").length (Kesish va bo'lish).
Yechim
const review = new File(
["Osh juda mazali edi. Rahmat, Bahor!"],
"sharh.txt",
{ type: "text/plain" },
);
const text = await review.text();
console.log(text.split(" ").length); // 6Bo'sh joylar bo'yicha bo'lsak — 6 bo'lak. Matnda ketma-ket ikki bo'sh joy bo'lsa, bo'sh "so'z" ham sanalardi — unda text.trim().split(/\s+/) ishonchliroq (RegExp asoslari).
2-mashq (o'rta): PDF tekshiruvi
Funksiya isPdf(file) yozing: fayl %PDF bilan boshlansa true. Uni ikki fayl bilan sinang: new File(["%PDF-1.7 ..."], "menyu.pdf") va new File(["salom"], "menyu.pdf").
Ishora: await file.slice(0, 4).text() — to'rtta belgi "%PDF" ga tengmi? %PDF — oddiy ASCII harflar, shuning uchun bytes() o'rniga text() ham yetadi.
Yechim
async function isPdf(file) {
const start = await file.slice(0, 4).text();
return start === "%PDF";
}
const real = new File(["%PDF-1.7 ..."], "menyu.pdf");
const fake = new File(["salom"], "menyu.pdf");
console.log(await isPdf(real)); // true
console.log(await isPdf(fake)); // falseIkkalasining nomi bir xil — farqni faqat baytlar ko'rsatadi. slice(0, 4) faylning qolganini o'qimaydi: 200 MB lik PDF ham bir zumda tekshiriladi.
3-mashq (qiyin): Avatarni saqlash
Mehmon profil rasmini tanlaydi (accept="image/*", 200 KB gacha). Rasm readAsDataURL bilan o'qilib, localStorage ga "avatar" kaliti bilan saqlansin va <img> da ko'rsatilsin. Sahifa qayta ochilganda (natija oynasida «Qayta ishga tushirish») rasm xotiradan chiqsin. Konsolga: Saqlandi: N belgi yoki Xotiradan olindi.
Ishora: bu yerda data URL to'g'ri tanlov — localStorage faqat satr saqlaydi, obyekt URL esa sahifa yopilganda o'ladi. 200 KB chegara — localStorage hajmi cheklangan (localStorage va sessionStorage).
Yechim
<style>
body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
#avatar { width: 96px; height: 96px; border-radius: 50%;
object-fit: cover; display: block; margin-top: 0.5rem; }
</style>
<label for="photo">Profil rasmi (200 KB gacha):</label>
<input type="file" id="photo" accept="image/*">
<p id="message" aria-live="polite"></p>
<script>
const MAX_SIZE = 200 * 1024;
const photo = document.getElementById("photo");
const message = document.getElementById("message");
const avatar = document.createElement("img");
avatar.id = "avatar";
avatar.alt = "Profil rasmi";
function showAvatar(dataUrl) {
avatar.src = dataUrl;
photo.after(avatar);
}
const saved = localStorage.getItem("avatar");
if (saved !== null) {
showAvatar(saved);
console.log("Xotiradan olindi");
}
photo.addEventListener("change", () => {
const [file] = photo.files;
if (file === undefined) return;
if (!file.type.startsWith("image/") || file.size > MAX_SIZE) {
message.textContent = "200 KB gacha rasm tanlang.";
return;
}
const reader = new FileReader();
reader.addEventListener("load", () => {
try {
localStorage.setItem("avatar", reader.result);
} catch (error) {
message.textContent = "Xotira to'la — rasm saqlanmadi.";
return;
}
showAvatar(reader.result);
message.textContent = "Rasm saqlandi.";
console.log(`Saqlandi: ${reader.result.length} belgi`);
});
reader.readAsDataURL(file);
});
</script>osh.png (5 747 bayt) bilan sinadik: Saqlandi: 7686 belgi. Natija oynasi localStorage o'rinbosarini beradi va uni blok uchun saqlaydi — «Qayta ishga tushirish» dan keyin Xotiradan olindi. Haqiqiy saytingizda — brauzerning o'z xotirasi. setItem try/catch ichida: xotira to'lsa QuotaExceededError otiladi (localStorage darsidagi qoida). file.type bu yerda faqat qulaylik uchun — saqlanadigan narsa baribir <img> da ko'rsatiladi.
4-mashq: Vazifalar qadami — import fayldan
O'tgan darsda vazifalar eksportni faylga yuklab berishni o'rgandi. Endi teskarisi: o'sha .json faylni tanlab, vazifalarni import qilish. Hozir import faqat textarea ga JSON matnini qo'yish bilan ishlaydi. Yangi maydon — fayl tanlash.
Tekshiruv bu darsdagi tartibda: avval nom va hajm (o'qishsiz), keyin await fayl.text(), keyin tarkibni mavjud paketniOqi tekshiradi. JSON'da sehrli baytlar yo'q — u oddiy matn. Shuning uchun "haqiqiy tur" tekshiruvi — paketniOqi ning o'zi: noto'g'ri JSON yoki noto'g'ri tuzilma rad etiladi.
1. Branch:
git switch -c feature/import-fayl2. fayl.js — o'qishdan oldingi tekshiruv (@param/@returns JSDoc qatorlari bu yerda qisqartirilgan, to'liq fayl — kanon repoda):
// 100 ta vazifa eksporti ~10 KB; 1 MB dan kattasi — boshqa fayl
export const MAKS_FAYL_HAJMI = 1_000_000;
/**
* O'qishdan OLDIN tekshiruv: nomi va hajmi — tarkibini paketniOqi
* tekshiradi. MIME turiga ishonmaymiz: ba'zi tizimlarda .json uchun
* file.type bo'sh keladi
*/
export function faylniTekshir(fayl) {
if (!fayl.name.toLowerCase().endsWith(".json")) {
return "Faqat .json fayl import qilinadi.";
}
if (fayl.size === 0) {
return "Fayl bo'sh.";
}
if (fayl.size > MAKS_FAYL_HAJMI) {
return "Fayl juda katta: 1 MB dan oshmasin.";
}
return null;
}Funksiya File ning faqat ikki xususiyatini ishlatadi: name va size. Shuning uchun uni Node'da ham, oddiy obyekt bilan ham testlash mumkin. toLowerCase() — ESKI.JSON ham o'tsin. MIME turi tekshirilmaydi — izohda sababi: ba'zi tizimlarda .json uchun file.type bo'sh keladi (file.type haqidagi qoidani eslang).
3. index.html — Import qismiga maydon:
<p>
<label for="import-fayl">Yoki eksport faylini tanlang</label>
<input type="file" id="import-fayl"
accept=".json,application/json">
</p>render.js ga: export const importFayl = document.getElementById("import-fayl");.
4. asosiy.js — importQil matnni parametr bilan oladi:
// Matn maydondan yoki fayldan keladi — tekshiruv va server bitta
function importQil(matn, maydon = importMatn) {
importXatosiniTozala();
elonQil("");
let malumotlar;
try {
malumotlar = paketniOqi(matn);
} catch (xato) {
if (xato instanceof VazifaXatosi) {
importXatosiniKorsat(xato.message, maydon);
return;
}
// … (qolgani o'zgarmagan)Oldin importQil() matnni importMatn.value dan o'zi olardi. Endi matn va "xatoni qaysi maydonda ko'rsatish" — parametr. Shunda fayl va textarea bitta yo'ldan o'tadi: bitta paketniOqi, bitta server sinxroni. Ikki nusxa kod yo'q. importTugma endi () => importQil(importMatn.value) ni chaqiradi.
5. asosiy.js — fayl tanlanganda:
async function faylniImportQil(fayl) {
importXatosiniTozala();
const xato = faylniTekshir(fayl);
if (xato !== null) {
importXatosiniKorsat(xato, importFayl);
return;
}
let matn;
try {
// File — Blob: text() Promise qaytaradi
matn = await fayl.text();
} catch {
importXatosiniKorsat("Faylni o'qib bo'lmadi.", importFayl);
return;
}
importQil(matn, importFayl);
}
importFayl.addEventListener("change", () => {
const [fayl] = importFayl.files;
if (fayl !== undefined) {
faylniImportQil(fayl);
}
// O'sha faylni qayta tanlash ham change hodisasini bersin
importFayl.value = "";
});Darsdagi tartib: tekshiruv (o'qishsiz) → await fayl.text() → tarkib. text() ham xato berishi mumkin — masalan, fayl tanlangandan keyin diskdan o'chirilgan bo'lsa (NotReadableError). Shuning uchun try/catch.
importFayl.value = "" — oxirgi qator. Busiz foydalanuvchi o'sha faylni tuzatib, qayta tanlasa, change kelmaydi: brauzer "tanlov o'zgarmadi" deb hisoblaydi. Fayl maydoniga faqat bo'sh satr yozish mumkin (Fayl yuklash maydoni darsidagi qoida).
6. render.js — xato qaysi maydondan bo'lsa, o'sha belgilanadi:
export function importXatosiniKorsat(xabar, maydon = importMatn) {
importXato.textContent = xabar;
maydon.setAttribute("aria-invalid", "true");
maydon.focus();
}
export function importXatosiniTozala() {
importXato.textContent = "";
importMatn.removeAttribute("aria-invalid");
importFayl.removeAttribute("aria-invalid");
}Fayldagi xatoda fokus fayl maydoniga o'tadi — Bobur (faqat klaviatura) qayerda xato bo'lganini darhol biladi.
7. Test. Yangi tekshiruv/fayl.test.js — 4 test. Node 24 da File global, shuning uchun haqiqiy File bilan:
test("kengaytma katta harfda ham, MIME bo'sh bo'lsa ham", () => {
assert.equal(faylniTekshir(new File(["{}"], "ESKI.JSON")), null);
});
test("boshqa fayl — rad", () => {
assert.equal(
faylniTekshir(new File(["salom"], "rasm.png")),
"Faqat .json fayl import qilinadi.",
);
});Qolgan ikkitasi — oddiy eksport fayli va chegara holatlari: bo'sh, aynan 1 000 000 bayt, bitta ortiq. 1 MB lik faylni yaratmaslik uchun ular oddiy obyekt beradi: { name: "a.json", size: MAKS_FAYL_HAJMI + 1 }. Funksiyaga faqat name va size kerak. Natija — 75/75; lint, format:check, tip — toza.
8. Brauzerda (Live Server). Kanon tekshiruvi (Chrome 154) fayl maydoniga haqiqiy fayllarni berdi:
✅ fayl import: .txt rad: "Faqat .json fayl import qilinadi."
✅ fayl import: e'lon: "5 ta vazifa import qilindi."
✅ fayl import: xato tozalandi, input bo'shatildi: ["",false,""]Yana ikki qator: versiyasi 2 bo'lgan fayl Versiya 2 o'qilmaydi — faqat 1. xatosini fayl maydonida ko'rsatdi; o'tgan darsda yuklab olingan eksport fayli import qilinganda o'sha 5 ta vazifa ekranda ham, serverda ham chiqdi. O'zingiz: «Faylga yuklab olish» → bir vazifani o'chiring → yuklangan faylni tanlang — vazifa qaytadi.
9. Commit va PR. To'liq xabar 94 belgi — Git sarlavhasi uchun uzun (≤ 72). Shuning uchun sarlavha va tanaga bo'lamiz:
git add assets/js/fayl.js assets/js/asosiy.js assets/js/render.js \
index.html tekshiruv/fayl.test.js
git commit -m "feat: import fayldan — kengaytma va hajm tekshiruvi" \
-m "file.text(); paketniOqi qayta ishlatiladi."
git push -u origin feature/import-fayl
gh pr create --fill
gh pr merge --mergeDiff: 5 fayl, +109 −9.
9. Real ishda
- Yuklash formalari. Avatar, hujjat, chek — tanlangan zahoti preview va "fayl juda katta" xabari. Gmail, Telegram, davlat xizmatlari portallari shunday qiladi.
- Import. CSV'dan kontaktlar, JSON'dan sozlamalar, Excel'dan narxlar — fayl brauzerda o'qiladi va tekshiriladi, serverga faqat toza ma'lumot ketadi. Katta CSV'lar uchun PapaParse kabi kutubxonalar ichkarida aynan
stream()yokislice()bilan bo'laklab o'qiydi. - Rasm bilan ishlash. Yuklashdan oldin rasmni kichraytirish (telefondagi 8 MB surat → 300 KB) —
createImageBitmap+<canvas>. Buni Canvas amaliyotda darsida qilamiz. - Intervyu. "
FileReadervablob.text()farqi?", "Rasm preview uchun data URL yoki obyekt URL?", "Fayl turini qanday ishonchli aniqlaysiz?" — tez-tez so'raladi.
Xulosa
text(),arrayBuffer(),bytes()— Promise bilan butun faylni;stream()— bo'laklab, katta fayllar uchun.FileReader— hodisali eski usul; natija faqatloadda;readAsDataURL— base64data:manzil (+33% hajm).- Preview uchun obyekt URL +
img.decode(); data URL faqat satr kerak bo'lganda. - Tekshiruv tartibi: hajm (o'qishsiz) → sehrli baytlar (
slice(0, 12)) → ko'rsatish. - Brauzerdagi tekshiruv — qulaylik; himoya serverda.
Keyingi dars: Fayl yuklash (upload) va progress — tekshirilgan faylni serverga yuborish, yuklanish foizini ko'rsatish va katta faylni bo'laklab yuklash.
Manbalar
- W3C: File API (
Blob.text(),stream(),FileReader) — w3.org/TR/FileAPI - MDN:
FileReader,Blob.bytes(),HTMLImageElement.decode()— developer.mozilla.org - WHATWG MIME Sniffing: "Matching an image type pattern" (sehrli baytlar jadvali) — mimesniff.spec.whatwg.org
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!