IlmHamroh
JavaScript Full-stack/13-qism. Brauzer API'lari, performans va xavfsizlik11/32-dars22 daqiqa
Mundarija (34)

JavaScript File va Blob: faylni yaratish, ko'rsatish va yuklab berish

Qisqacha: Blob — brauzer xotirasidagi baytlar bo'lagi, unda hajm (size) va tur (type) bor. Fayl obyekti (File) — nomi va o'zgartirilgan sanasi qo'shilgan Blob; uni <input type="file"> beradi yoki new File() bilan o'zingiz yaratasiz. URL.createObjectURL(blob) bo'lakka vaqtinchalik blob: manzil beradi — uni <img src> ga qo'yib ko'rsatasiz yoki <a download> bilan fayl qilib yuklab berasiz. Ish tugagach manzil URL.revokeObjectURL() bilan bo'shatiladi.

Bu darsda

  • <input type="file"> dan tanlangan fayllarni olib, name, size, type, lastModified ni o'qiy olasiz.
  • new Blob() va new File() bilan xotirada fayl yarata olasiz, slice() bilan bo'lak kesasiz.
  • URL.createObjectURL bilan faylni sahifada ko'rsatasiz va revokeObjectURL bilan xotirani bo'shatasiz.
  • Serversiz, <a download> bilan foydalanuvchiga fayl yuklab bera olasiz.
  • vazifalar ga "Faylga yuklab olish" tugmasini qo'shasiz.

Oldin bilishingiz kerak: Fayl yuklash maydoni, FormData, Typed arrays va binar ma'lumot, Matnli va binar fayllar.

1. Nega bu kerak?

Shu darsdan 13-qismning "fayllar" bloki boshlanadi: to'rt dars ketma-ket. Bugun — fayl nima ekanini va uni qanday yaratishni. Keyingi darslarda — faylni o'qish, serverga yuklash va sudrab tashlash.

«Bahor»da ikki vazifa bor. Birinchisi — Jasur akaning iltimosi. U menyuni Excel'da ochmoqchi: "Saytda bitta tugma bo'lsin, bossam — menyu fayl bo'lib tushsin". Sardorning birinchi fikri — serverda fayl yasash. Lekin menyu allaqachon brauzerda, JavaScript massivida turibdi. Uni serverga yuborib, fayl qilib, qaytarib olish — ortiqcha yo'l.

Ikkinchisi — mehmonlar tomonda. Fayl yuklash maydoni darsida bron formasiga to'lov cheki maydonini qo'shgan edik. Mehmon chekni tanlaydi, lekin nimani tanlaganini ko'rmaydi — faqat fayl nomi. Xato rasm yuborib qo'yishi mumkin.

Ikkala vazifaning kaliti bitta: brauzer fayllar bilan o'zi ishlay oladi. Faylni xotirada yaratish, tanlangan faylni ko'rsatish, uni yuklab berish — serversiz. Bularning hammasi ikki obyekt atrofida qurilgan: Blob va File.

2. Tanlangan fayl: <input type="file"> dan File gacha

2.1 input.files

Fayl yuklash maydoni darsida <input type="file"> ni HTML tomondan ko'rdik. JavaScript'da tanlangan fayllar maydonning files xususiyatida turadi. Foydalanuvchi fayl tanlaganda maydon change hodisasini beradi (input va change darsi).

Fayl tanlang (bir nechtasini ham bo'ladi) — oynada uning ma'lumotlari chiqadi:

html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  li { margin: 0.25rem 0; }
</style>
<label for="files">Fayl tanlang:</label>
<input type="file" id="files" multiple>
<ul id="info"></ul>
<script>
  const input = document.getElementById("files");
  const info = document.getElementById("info");

  function formatSize(bytes) {
    if (bytes < 1024) return `${bytes} B`;
    if (bytes < 1024 ** 2) return `${(bytes / 1024).toFixed(1)} KB`;
    return `${(bytes / 1024 ** 2).toFixed(1)} MB`;
  }

  input.addEventListener("change", () => {
    info.replaceChildren();
    for (const file of input.files) {
      const item = document.createElement("li");
      const date = new Date(file.lastModified);
      const changed = date.toLocaleString("uz");
      item.textContent = `${file.name} — ${formatSize(file.size)},`
        + ` tur: ${file.type || "noma'lum"}, o'zgargan: ${changed}`;
      info.append(item);
    }
    console.log(`${input.files.length} ta fayl tanlandi`);
  });
</script>

Har faylda to'rtta asosiy xususiyat bor:

Xususiyat Nima Misol
name fayl nomi (papkasiz) "chek.jpg"
size hajmi, baytda 482133
type MIME turi "image/jpeg"
lastModified oxirgi o'zgargan vaqt, ms 1791201600000

formatSize — baytlarni odam o'qiydigan ko'rinishga keltiradi: 1 KB = 1 024 bayt, 1 MB = 1 024 KB (1024 ** 2 — 1 024 ning kvadrati).

Biz ikki fayl tanlab sinadik: 900 000 baytli chek.bin va 11 baytli a.txt. Ro'yxatda chek.bin — 878.9 KB, tur: application/octet-stream va a.txt — 11 B, tur: text/plain chiqdi, konsolda esa 2 ta fayl tanlandi. 900 000 ÷ 1 024 ≈ 878,9 — shuning uchun KB soni "yumaloq" emas.

Ikki muhim narsa:

  • Faylning yo'li yo'q. name — faqat nom. Diskdagi to'liq yo'l (C:\Users\...) sahifaga berilmaydi — bu foydalanuvchining shaxsiy ma'lumoti.
  • Fayl hech qayerga ketmadi. Hammasi brauzer ichida. Server faqat siz fetch bilan yuborsangiz ko'radi (keyingi darslarda).

2.2 FileList — massiv emas

input.files — FileList obyekti. U length va for...of ni biladi, lekin massiv emas. map, filter kabi metodlari yo'q:

js
const names = input.files.map((file) => file.name);

Chrome 154 da:

text
TypeError: input.files.map is not a function

Tarjimasi: "input.files.map — funksiya emas". FileList da map degan narsa umuman yo'q, undefined ni chaqirib bo'lmaydi. Tuzatish: avval haqiqiy massivga aylantiring — [...input.files].map(...) yoki Array.from(input.files) (Massivga o'xshash obyektlar darsidagi usullar).

2.3 type — kengaytmadan taxmin

file.type ni fayl ichidan o'qib aniqlanmaydi. Brauzer uni fayl nomining kengaytmasidan taxmin qiladi. virus.exe ni rasm.jpg deb qayta nomlasangiz — type "image/jpeg" bo'ladi. Notanish kengaytmada esa bo'sh satr: "".

Shuning uchun type — faqat foydalanuvchiga qulaylik ("siz rasm tanlamadingiz"). Himoya emas. Faylning haqiqiy turini baytlaridan tekshirishni keyingi darsda ko'ramiz.

Tekshirib ko'ring: Foydalanuvchi menyu.xlsx faylini tanladi, lekin uning kompyuterida Excel o'rnatilmagan. file.type nimaga teng bo'lishi mumkin?

Javob

Kengaytma ma'lum bo'lsa — "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", aks holda bo'sh satr "". Natija tizim va brauzerga bog'liq. Shuning uchun type ga to'liq tayanmang: bo'sh bo'lishi mumkinligini hisobga oling.

3. Blob: baytlar qutisi

3.1 Blob nima

Blob (inglizcha "Binary Large OBject" — katta binar obyekt) — baytlar bo'lagi, ustiga ikki yorliq yopishtirilgan: hajmi va turi. Ichidagi baytlarni to'g'ridan-to'g'ri o'qib bo'lmaydi — buning uchun maxsus usullar bor (keyingi darsda).

O'xshatish: Blob — yopiq, tasmalangan pochta qutisi. Ustida "3 kg, ichida: kitob" degan yorliq bor. Qutini boshqa joyga uzatish, ikkiga bo'lish, unga manzil yozish mumkin. Ichiga qarash uchun esa uni ochish kerak.

File — xuddi shu quti, faqat yorlig'ida yana nom va sana bor. File — Blob ning "farzandi": u Blob dan meros oladi (prototip zanjiri darsidagi kabi). Shuning uchun Blob qabul qiladigan har joyga File ni ham berish mumkin.

File va Blob Node 24 da ham bor. Shuning uchun quyidagi misollarni shu yerning o'zida ishga tushira olasiz:

js
const receipt = new File(["Osh — 35 000 so'm"], "chek.txt", {
  type: "text/plain",
  lastModified: new Date("2026-10-05T12:00:00Z").getTime(),
});

console.log(receipt.name); // chek.txt
console.log(receipt.size); // 19
console.log(receipt.type); // text/plain
console.log(receipt instanceof Blob); // true

new File(qismlar, nom, sozlamalar) — uch argument: tarkib qismlari ro'yxatda, fayl nomi va { type, lastModified }. lastModified ni bermasangiz — hozirgi vaqt olinadi.

3.2 size — belgilarda emas, baytlarda

"Osh — 35 000 so'm" — 17 belgi. size esa 19. Nega?

Matnli va binar fayllar darsini eslang: matn faylga UTF-8 bilan yoziladi. Lotin harflari va raqamlar 1 baytdan, uzun tire — esa 3 bayt. 16 + 3 = 19. Emoji bilan farq yana ham ko'p:

js
const label = "Osh 🍚";
console.log(label.length); // 6
console.log(new Blob([label]).size); // 8

length UTF-16 birliklarini sanaydi (emoji — 2 ta), size esa UTF-8 baytlarini (emoji — 4 bayt). Hisoblang: O, s, h va bo'sh joy — 4 bayt, emoji — yana 4: jami 8. Server "1 MB gacha" deganda — baytlarni nazarda tutadi. Hajmni doim size bilan tekshiring.

3.3 Qismlardan yig'ish

new Blob(qismlar, { type }) ga turli narsalarni aralash berish mumkin: satrlar, typed array'lar va boshqa Blob'lar. Ular ketma-ket bitta baytlar qatoriga yopishtiriladi:

js
const greeting = new Blob(
  ["Bahor", new Uint8Array([32, 79, 75]), new Blob(["!"])],
  { type: "text/plain" },
);

console.log(greeting.size); // 9
console.log(await greeting.text()); // Bahor OK!

[32, 79, 75] — bo'sh joy, O va K harflarining kodlari. text() — Blob'ni ochib, ichini matn sifatida o'qiydi. U Promise qaytaradi, shuning uchun await. O'qish usullarini to'liq keyingi darsda ko'ramiz.

3.4 slice(): bo'lak kesish

blob.slice(boshi, oxiri, tur) — yangi, kichikroq Blob. Xuddi satrning slice i kabi: oxiri kirmaydi, manfiy son oxirdan sanaydi:

js
const greeting = new Blob(["Bahor OK!"], { type: "text/plain" });
const first = greeting.slice(0, 5);
const last = greeting.slice(-3);

console.log(first.size, await first.text()); // 5 Bahor
console.log(last.size, await last.text()); // 3 OK!
console.log(JSON.stringify(first.type)); // ""

Ikki narsaga e'tibor bering. Birinchisi — kesilgan bo'lakning type i bo'sh: uchinchi argument berilmasa, tur ko'chmaydi. Ikkinchisi muhimroq: slice baytlarni nusxalamaydi. 2 GB lik videodan bo'lak kesish ham bir zumda bo'ladi — yangi Blob shunchaki "asl qutining 100-baytidan 200-baytigacha" degan yozuv. Katta faylni bo'laklab serverga yuborish shunga asoslanadi (Fayl yuklash va progress darsi).

Bo'sh joyni to'ldiring: new Blob(["abc"]).slice(1).size ning qiymati

Tekshirib ko'ring: new Blob(["salom"]) uchun blob.name nima qaytaradi?

Javob

undefined. Nom faqat File da bor. Blob — nomsiz quti. Sinovda Node 24 da ham name, ham lastModified undefined chiqdi. Nom kerak bo'lsa — new File([blob], "nomi.txt").

4. Obyekt URL: Blob'ga manzil berish

4.1 URL.createObjectURL()

Blob xotirada turibdi. Uni <img> ga ko'rsatish yoki havola qilish uchun manzil kerak — src va href faqat URL qabul qiladi.

URL.createObjectURL(blob) shunday manzil yaratadi. U obyekt URL (object URL) deyiladi va blob: bilan boshlanadi:

text
blob:https://example.com/3f1c9a0e-8d2b-4c6f-9e51-7a2d4b8c1e90

Tarkibi: blob: + sahifaning origin'i + tasodifiy identifikator. Manzil faqat shu sahifa ichida ishlaydi. Uni boshqa tabga yoki do'stingizga yuborsangiz — ochilmaydi. U brauzer xotirasidagi Blob'ga ishora, xolos. Natija oynasida origin null bo'lgani uchun manzil blob:null/… ko'rinishida bo'ladi.

Blob faqat fayl tanlash maydonidan yoki new Blob() dan kelmaydi. fetch asoslari darsida "blob() bilan rasm va fayllarni keyin ishlatamiz" degan edik: javob tanasini response.blob() bilan Blob qilib olish mumkin. Mashq serveridagi /fayl 2 000 baytlik fayl beradi:

js
const response = await fetch(
  "https://ilmhamroh.uz/api/mashq/fayl?hajm=2000&oraliq=0",
);
const blob = await response.blob();
console.log(blob.size); // 2000
console.log(blob.type); // application/octet-stream

type server yuborgan Content-Type sarlavhasidan olinadi. Brauzerda bunday Blob'ga ham obyekt URL berib, rasm bo'lsa <img> da ko'rsatish yoki yuklab berish mumkin.

4.2 «Bahor»: menyu belgisi xotiradan

Jasur aka har taom uchun kichik belgi xohlaydi: rangli doira va ichida harf. Rasm faylini chizish shart emas — SVG matnini Blob qilamiz va <img> ga beramiz:

html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  img { width: 96px; height: 96px; display: block;
    margin-top: 0.75rem; }
  input, button { font: inherit; padding: 0.3rem 0.6rem; }
</style>
<label for="dish">Taom nomi:</label>
<input id="dish" value="Osh" maxlength="20">
<button id="make" type="button">Belgi yasash</button>
<img id="badge" alt="">
<p id="url"></p>
<script>
  const dishInput = document.getElementById("dish");
  const badge = document.getElementById("badge");
  let currentUrl = null;

  function makeBadge() {
    const name = dishInput.value.trim();
    const letter = name.charAt(0).toUpperCase() || "?";
    const svg = `<svg xmlns="http://www.w3.org/2000/svg"
      width="96" height="96" viewBox="0 0 96 96">
      <circle cx="48" cy="48" r="46" fill="#2e7d32"/>
      <text x="48" y="62" font-size="44"
      text-anchor="middle" fill="#fff"
      font-family="sans-serif">${letter}</text></svg>`;
    const blob = new Blob([svg], { type: "image/svg+xml" });

    if (currentUrl !== null) URL.revokeObjectURL(currentUrl);
    currentUrl = URL.createObjectURL(blob);
    badge.src = currentUrl;
    badge.alt = `${dishInput.value} belgisi`;
    document.getElementById("url").textContent = currentUrl;
    console.log(`Blob: ${blob.size} bayt, ${blob.type}`);
  }

  const makeButton = document.getElementById("make");
  makeButton.addEventListener("click", makeBadge);
  makeBadge();
</script>

Qatorma-qator:

  • svg — oddiy matn: doira va harf. ${letter} — taom nomining birinchi harfi.
  • new Blob([svg], { type: "image/svg+xml" }) — matn rasm faylga aylandi. Tur muhim: brauzer <img> da Blob'ni shu tur bo'yicha o'qiydi.
  • URL.createObjectURL(blob) — Blob'ga manzil. badge.src ga beriladi va rasm chiqadi.
  • URL.revokeObjectURL(currentUrl) — eski manzilni bo'shatish. Bu haqda pastda batafsil.

Sahifa ochilganda konsolda:

text
Blob: 269 bayt, image/svg+xml

Taom nomini "Manti" ga o'zgartirib, tugmani bosing — belgida "M" chiqadi. Manzil har safar yangi. Biz sinadik: rasm haqiqatan yuklandi (naturalWidth 96), manzil blob:null/ bilan boshlandi.

4.3 revokeObjectURL: xotirani qaytarish

Obyekt URL yaratilgach, brauzer Blob'ni xotirada ushlab turadi — manzil kimgadir kerak bo'lib qolishi mumkin. Sahifa yopilguncha yoki siz aytguncha. 100 ta rasm ko'rsatdingiz va revoke qilmadingiz — 100 ta Blob xotirada qoladi, hatto <img> lar o'chirilgan bo'lsa ham. Bu xotira sizishi (memory leak) — Xotira sizishlari darsidagi muammo.

URL.revokeObjectURL(url) — "bu manzil endi kerak emas". Shundan keyin manzil ishlamaydi, Blob'ni esa (boshqa hech kim ushlamasa) axlat yig'uvchi tozalaydi.

Qachon chaqirish kerak:

  • <img> uchun — rasm yuklangach (load hodisasi) yoki rasm almashtirilganda. Yuqoridagi misolda — yangi belgi yasalganda eskisi bo'shatiladi.
  • Yuklab berish uchun — yuklash boshlangandan keyin (keyingi bo'limda).
flowchart LR
  B["Blob<br/>(xotirada)"] -->|createObjectURL| U["blob: manzil"]
  U --> I["img src"]
  U --> A["a download"]
  U -->|revokeObjectURL| X["manzil o'chdi,<br/>xotira bo'shaydi"]

Nimaga qarang: manzil — Blob bilan foydalanuvchi o'rtasidagi "ko'prik". Ish tugagach ko'prikni olib tashlash sizning vazifangiz.

Tekshirib ko'ring: img.src = URL.createObjectURL(blob) dan keyin darhol, keyingi qatorda URL.revokeObjectURL(img.src) yozildi. Rasm chiqadimi?

Javob

Yo'q. Biz Chrome 154 da sinadik: rasm load emas, error hodisasini oldi. src ni o'rnatish rasmni darhol yuklamaydi: yuklash keyinroq, alohida task'da boshlanadi. U paytda manzil allaqachon bekor qilingan. To'g'ri yo'l — img.addEventListener("load", () => URL.revokeObjectURL(img.src), { once: true }).

5. Yuklab berish: <a download>

5.1 download atributi

Havolaga download atributi qo'shilsa, brauzer manzilni ochmaydi, faylni yuklab oladi. Atribut qiymati — taklif qilinadigan fayl nomi:

html
<a href="menyu.pdf" download="bahor-menyu.pdf">
  Menyuni yuklab olish
</a>

download faqat shu saytdan yoki blob: / data: manzillar uchun ishlaydi. Boshqa saytdagi faylga qo'yilsa, brauzer uni e'tiborsiz qoldiradi va oddiy havola kabi ochadi. Bu himoya: begona sayt faylini "siznikiday" nom bilan tushirib bo'lmaydi.

Bizning Blob'ning manzili blob: — shuning uchun mos keladi. Retsept: Blob → obyekt URL → vaqtinchalik <a download> → click() → revoke.

5.2 «Bahor»: menyuni CSV qilib yuklab olish

CSV (Comma-Separated Values — vergul bilan ajratilgan qiymatlar) — jadvalni oddiy matn qilib yozish formati. Har qator — jadval qatori, ustunlar ajratgich bilan. Excel uni ochadi. Jasur akaning tugmasi:

html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  button { font: inherit; padding: 0.4rem 0.8rem; }
</style>
<p>«Bahor» menyusi: 3 ta taom</p>
<button id="download" type="button">
  Menyuni CSV qilib yuklab olish
</button>
<script>
  const menu = [
    { name: "Osh", price: 35000 },
    { name: "Manti", price: 30000 },
    { name: "Lag'mon", price: 28000 },
  ];

  function downloadFile(content, fileName, type) {
    const blob = new Blob(content, { type });
    const url = URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.href = url;
    link.download = fileName;
    link.click();
    setTimeout(() => URL.revokeObjectURL(url), 0);
    console.log(`${fileName}: ${blob.size} bayt`);
  }

  const button = document.getElementById("download");
  button.addEventListener("click", () => {
    const rows = menu.map((dish) => `${dish.name};${dish.price}`);
    const csv = ["Taom;Narx", ...rows].join("\r\n");
    // BOM: Excel faylni UTF-8 deb taniydi (o' va g' buzilmaydi)
    downloadFile(["\uFEFF", csv], "bahor-menyu.csv",
      "text/csv;charset=utf-8");
  });
</script>

Qatorma-qator:

  • downloadFile — qayta ishlatiladigan funksiya: tarkib qismlari, nom, tur.
  • document.createElement("a") — havola sahifaga qo'shilmaydi. link.click() uni shunchaki "bosadi" — DOM'da bo'lishi shart emas.
  • setTimeout(() => URL.revokeObjectURL(url), 0) — darhol emas, keyingi task'da bo'shatamiz. click() yuklashni boshlaydi, lekin brauzer manzilni o'qishga ulgurishi kerak. Keyingi task'gacha ulguradi.
  • join("\r\n") — qatorlar orasiga Windows qator oxiri (CRLF) qo'yiladi. CSV standarti aynan shuni tavsiya qiladi (Qator oxiri (CRLF/LF) darsi).
  • "\uFEFF" — BOM (Byte Order Mark — "bayt tartibi belgisi"). Ko'rinmaydigan belgi, UTF-8 da 3 bayt. Excel uni ko'rib faylni UTF-8 deb taniydi. BOM'siz Excel faylni eski kodlashda o'qiydi: lotin harflari va apostrof to'g'ri chiqadi, lekin —, «» yoki kirill harflari buziladi. Ertaga menyuga shunday belgi qo'shilsa ham fayl buzilmasin — BOM doim qo'yiladi.
  • ; ajratgich — o'nlik kasrni vergul bilan yozadigan til sozlamalarida (rus, o'zbek) Windows'dagi Excel ko'pincha vergulni emas, nuqtali vergulni kutadi.

Tugmani bosganingizda brauzer bahor-menyu.csv faylini yuklab oladi va konsolga uning hajmi chiqadi:

text
bahor-menyu.csv: 51 bayt

Biz buni Chrome 154 da, yuklab olingan faylni ochib sinadik: fayl bahor-menyu.csv nomi bilan, 51 bayt bo'lib tushdi, boshida EF BB BF (BOM) baytlari bor edi. Sahifa hech qayerga o'tmadi. Natija oynasi allow-downloads ruxsati bilan ochilgan — shuning uchun bu yerda ham yuklab olish ishlaydi.

Server bitta ham so'rov olmadi. Ma'lumot brauzerda edi — fayl ham brauzerda yasaldi.

Diqqat: link.click() ni foydalanuvchi bosishidan tashqarida (masalan, sahifa ochilishi bilan, taymerda) chaqirsangiz, brauzer "sayt fayl yuklamoqchi" deb ruxsat so'rashi yoki bloklashi mumkin. Yuklab berish — doim foydalanuvchi harakatiga javob.

Tekshirib ko'ring: link.download = "menyu.csv" qatorini olib tashlasak nima bo'ladi?

Javob

Havola oddiy havolaga aylanadi: brauzer blob: manzilni yangi sahifa sifatida ochishga urinadi. CSV matn sifatida ko'rsatiladi yoki nomi tasodifiy identifikator bo'lgan fayl tushadi — brauzerga bog'liq. download — "och emas, saqla" degan buyruq.

5.3 Obyekt URL yoki data: URL

Blob'ga manzil berishning yana bir yo'li — data URL: butun tarkib manzilning o'zida, data:image/svg+xml;base64,PHN2Zy... ko'rinishida. Uni keyingi darsda FileReader bilan olamiz. Hozircha farqi:

Obyekt URL (blob:) Data URL (data:)
Uzunligi ~60 belgi fayl hajmidan ~33% katta
Yaratish darhol, nusxasiz butun faylni o'qib, kodlaydi
Yashash muddati sahifa yopilguncha yoki revoke doim (oddiy satr)
Qayerda faqat shu sahifada saqlash, yuborish mumkin

Sahifada ko'rsatish va yuklab berish uchun — obyekt URL. Satr sifatida saqlash kerak bo'lsa (masalan, localStorage ga) — data URL.

6. Hujumchi nigohi

Fayl nomi va turi — foydalanuvchidan keladi. Demak, ular foydalanuvchi kiritgan matn kabi xavfli.

Hujum: nomdagi HTML. Hujumchi faylini shunday nomlaydi: <img src=x onerror=alert(1)>.jpg. Windows bunday nomga ruxsat bermaydi, lekin macOS va Linux'da < va > nomda bo'lishi mumkin. Sardor fayllar ro'yxatini innerHTML bilan chizsa:

js
// ❌ fayl nomi HTML sifatida o'qiladi
info.innerHTML += `<li>${file.name}</li>`;

— nomdagi <img onerror> ishga tushadi. Bu XSS: begona kod sizning sahifangizda bajariladi. Fayllar ro'yxati keyinchalik boshqa foydalanuvchiga (masalan, adminga) ko'rsatilsa — uning brauzerida.

Himoya: nom — doim textContent bilan, xuddi «Fayl tanlang» misolidagidek. textContent hech narsani HTML deb o'qimaydi.

Hujum: soxta tur. type kengaytmadan olinadi — u hech narsani kafolatlamaydi. SVG Blob'ni <img> da ko'rsatish xavfsiz: <img> ichidagi SVG skriptlari ishlamaydi. Lekin o'sha SVG'ni innerHTML bilan sahifaga qo'ysangiz yoki obyekt URL'ini yangi tabda ochsangiz — skript ishlashi mumkin. Foydalanuvchi faylini faqat <img>, <video> kabi "xavfsiz idish"da ko'rsating.

Server tomonidagi himoya — baribir majburiy (Fayl yuklash xavfsizligi darsi, backend qismida).

7. Ko'p uchraydigan xatolar

7.1 FileList ni massiv deb o'ylash

input.files.map(...) → TypeError: ... is not a function. Tuzatish: [...input.files].

7.2 Hajmni belgilarda hisoblash

text.length — belgilar, blob.size — baytlar. O'zbekcha matnda farq kam, emoji va boshqa alifbolarda katta. Tuzatish: chegarani size bilan tekshiring.

7.3 revokeObjectURL ni unutish

Har yangi rasmda yangi manzil, eskisi bo'shatilmaydi — xotira o'sadi. Tuzatish: manzilni o'zgaruvchida saqlang va kerak bo'lmay qolganda revoke qiling.

7.4 Juda erta revoke

click() yoki src dan keyin darhol revoke — yuklash ulgurmaydi. Tuzatish: setTimeout(..., 0) yoki load hodisasi.

7.5 Boshqa saytdagi faylga download

<a href="https://boshqa-sayt.example/menyu.pdf" download> — fayl ochiladi, yuklanmaydi. Tuzatish: faylni o'z serveringizdan bering yoki fetch → blob() → obyekt URL. Ikkinchi yo'l faqat boshqa sayt CORS bilan ruxsat bersa ishlaydi.

8. Mashqlar

1-mashq (oson): Matn fayl

Node'da (shu yerda) new File() bilan "eslatma.txt" faylini yarating, tarkibi — "Ertaga 10:00 da bron", turi — "text/plain". Uning nomi, hajmi va matnini chiqaring.

Yechim
js
const note = new File(["Ertaga 10:00 da bron"], "eslatma.txt", {
  type: "text/plain",
});

console.log(note.name); // eslatma.txt
console.log(note.size); // 20
console.log(await note.text()); // Ertaga 10:00 da bron

Matnda faqat lotin harflari, raqamlar va bo'sh joy — har biri 1 bayt. Shuning uchun size belgilar soniga teng.

2-mashq (o'rta): Bronlar JSON fayli

Bronlar massivini bronlar.json fayli qilib yuklab beradigan tugma yozing. JSON chiroyli (2 bo'sh joy bilan) formatlansin. «Menyuni CSV qilib yuklab olish» misolidagi downloadFile naqshini ishlating, turi — "application/json".

Ishora: JSON.stringify(qiymat, null, 2) (JSON asoslari darsi).

Yechim
html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  button { font: inherit; padding: 0.4rem 0.8rem; }
</style>
<button id="save" type="button">Bronlarni yuklab olish</button>
<script>
  const bookings = [
    { guest: "Malika", time: "19:00", people: 4 },
    { guest: "Bobur", time: "20:30", people: 2 },
  ];

  function downloadFile(content, fileName, type) {
    const blob = new Blob(content, { type });
    const url = URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.href = url;
    link.download = fileName;
    link.click();
    setTimeout(() => URL.revokeObjectURL(url), 0);
    console.log(`${fileName}: ${blob.size} bayt`);
  }

  document.getElementById("save").addEventListener("click", () => {
    const json = JSON.stringify(bookings, null, 2);
    downloadFile([json], "bronlar.json", "application/json");
  });
</script>

Tugmani bosganingizda brauzer bronlar.json ni yuklab oladi, konsolda bronlar.json: 139 bayt chiqadi (bosib sinalgan). JSON uchun BOM kerak emas: JSON standarti UTF-8 ni o'zi talab qiladi.

3-mashq (qiyin): Rasmlar galereyasi

Mehmon bir nechta rasm tanlaydi (multiple, accept="image/*"). Har biri kichik <img> bo'lib chiqsin (kengligi 120 px), ostida nomi. Talablar:

  • Rasm bo'lmagan fayl (type "image/" bilan boshlanmasa) o'tkazib yuborilsin.
  • Har rasm yuklangach, uning obyekt URL'i bo'shatilsin.
  • Nom textContent bilan yozilsin.
  • Konsolga: Ko'rsatildi: N ta rasm.

Ishora: img.addEventListener("load", ..., { once: true }) (Listener opsiyalari), file.type.startsWith("image/").

Yechim
html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  #gallery { display: flex; flex-wrap: wrap; gap: 0.75rem;
    margin-top: 0.75rem; }
  figure { margin: 0; width: 120px; }
  figure img { width: 120px; height: 90px; object-fit: cover; }
  figcaption { font-size: 0.8rem; overflow-wrap: anywhere; }
</style>
<label for="photos">Rasmlar:</label>
<input type="file" id="photos" accept="image/*" multiple>
<div id="gallery"></div>
<script>
  const photos = document.getElementById("photos");
  const gallery = document.getElementById("gallery");

  photos.addEventListener("change", () => {
    gallery.replaceChildren();
    const images = [...photos.files]
      .filter((file) => file.type.startsWith("image/"));

    for (const file of images) {
      const figure = document.createElement("figure");
      const img = document.createElement("img");
      const caption = document.createElement("figcaption");
      const url = URL.createObjectURL(file);
      img.addEventListener("load", () => URL.revokeObjectURL(url),
        { once: true });
      img.src = url;
      img.alt = file.name;
      caption.textContent = file.name;
      figure.append(img, caption);
      gallery.append(figure);
    }
    console.log(`Ko'rsatildi: ${images.length} ta rasm`);
  });
</script>

[...photos.files] — FileList massivga aylandi, endi filter bor. load dan keyin revoke: rasm allaqachon chizilgan, manzil endi kerak emas. Biz PNG, JPG va .txt fayllarini tanlab sinadik: Ko'rsatildi: 2 ta rasm.

4-mashq: Vazifalar qadami — eksport faylga

vazifalar da "Eksport" qismi bor: «JSON qilib chiqarish» matnni textarea ga yozadi, foydalanuvchi uni nusxalaydi. Endi yonida «Faylga yuklab olish» tugmasi bo'ladi: bosilsa, o'sha JSON vazifalar-2026-10-05.json kabi fayl bo'lib tushadi. Fayl nomida — bugungi mahalliy sana.

vazifalar kanoni o'zbekcha nomlar bilan yozilgan — downloadFile bu yerda faylniYuklabBer.

1. Branch:

bash
git switch -c feature/eksport-fayl

2. Yangi fayl assets/js/fayl.js:

js
// @ts-check
// fayl.js — fayl yaratish va o'qish (Blob, File)

/**
 * Matnni fayl qilib beradi: Blob → vaqtinchalik blob: URL →
 * <a download> bosiladi. Sahifa hech qayerga o'tmaydi.
 * @param {string} matn
 * @param {string} nomi foydalanuvchi ko'radigan fayl nomi
 * @param {string} [tur] MIME turi
 */
export function faylniYuklabBer(
  matn,
  nomi,
  tur = "application/json",
) {
  const blob = new Blob([matn], { type: tur });
  const url = URL.createObjectURL(blob);
  const havola = document.createElement("a");
  havola.href = url;
  havola.download = nomi;
  havola.click();
  // blob: URL Blob'ni xotirada ushlab turadi — bo'shatamiz. Keyingi
  // vazifada: yuklash shu paytgacha boshlangan bo'ladi
  setTimeout(() => URL.revokeObjectURL(url), 0);
}

Telefon ekraniga sig'ishi uchun parametrlar bu yerda alohida qatorlarda; kanon faylda ular bitta qatorda (Prettier shunday formatlaydi). Darsdagi downloadFile bilan solishtiring — bir xil retsept. Izohdagi "keyingi vazifada" — setTimeout(..., 0) ning keyingi task'i (kanon izohlari o'zbekcha). Fayl jsconfig.json ning include ro'yxatiga qo'shiladi — npm run tip uni ham tekshirsin.

3. paket.js — fayl nomi:

js
/**
 * Eksport fayli nomi — sana bilan, tartiblaganda xronologik turadi.
 * Mahalliy sana (UTC emas): kechasi soat 2 da olingan fayl kechagi
 * sanani olmasin
 * @param {Date} [sana]
 * @returns {string} masalan "vazifalar-2026-10-05.json"
 */
export function eksportFaylNomi(sana = new Date()) {
  const oy = String(sana.getMonth() + 1).padStart(2, "0");
  const kun = String(sana.getDate()).padStart(2, "0");
  return `vazifalar-${sana.getFullYear()}-${oy}-${kun}.json`;
}

Nega toISOString().slice(0, 10) emas? U UTC sanasini beradi. Toshkent UTC+5: 5-oktabr kechasi soat 2 da UTC'da hali 4-oktabr. Fayl kechagi sana bilan tushardi (Date va vaqt zonalari darsidagi tuzoq). getMonth() va getDate() — mahalliy.

4. index.html — tugma va oldindan yuklash:

html
<button type="button"
        id="eksport-fayl">Faylga yuklab olish</button>
html
<link rel="modulepreload" href="assets/js/fayl.js">

render.js ga: export const eksportFayl = document.getElementById("eksport-fayl");.

5. asosiy.js:

js
import {
  eksportMatni,
  eksportFaylNomi,
  paketniOqi,
} from "./paket.js";
import { faylniYuklabBer } from "./fayl.js";

eksportFayl.addEventListener("click", () => {
  const nomi = eksportFaylNomi();
  faylniYuklabBer(eksportMatni(holat.vazifalar), nomi);
  elonQil(`${nomi} yuklab olindi.`);
});

Fayl tarkibi — «JSON qilib chiqarish» dagi eksportMatni ning o'zi. Ya'ni yuklangan faylni keyingi darsda import qilish mumkin bo'ladi. elonQil — ekran o'quvchi uchun e'lon: Malika (NVDA bilan) yuklash bo'lganini eshitadi.

6. Test. tekshiruv/paket.test.js ga bitta test:

js
describe("eksportFaylNomi", () => {
  test("mahalliy sana, oy va kun ikki xonali", () => {
    assert.equal(
      eksportFaylNomi(new Date(2026, 9, 5, 2, 30)),
      "vazifalar-2026-10-05.json",
    );
    assert.equal(
      eksportFaylNomi(new Date(2027, 0, 9)),
      "vazifalar-2027-01-09.json",
    );
  });
});

new Date(2026, 9, 5, 2, 30) — mahalliy vaqt bo'yicha 5-oktabr, soat 02:30 (oylar 0 dan: 9 — oktabr). Aynan "kechasi soat 2" holati. Natija — 71/71; lint, format:check, tip — toza.

7. Brauzerda (Live Server). Kanon tekshiruvida (Chrome 154, yuklab olingan fayl bilan):

text
✅ eksport fayl: nomi: ["vazifalar-2026-10-05.json"]
✅ eksport fayl: tarkibi = JSON matni: true
✅ eksport fayl: e'lon: "vazifalar-2026-10-05.json yuklab olindi."
✅ eksport fayl: sahifa joyida: "/"

Yana bitta qator Blob turi application/json ekanini va blob: manzil bo'shatilganini tekshiradi. O'zingiz: tugmani bosing, yuklangan faylni VS Code'da oching — ichida «JSON qilib chiqarish» dagi matn. Brauzer manzil qatori o'zgarmaganiga e'tibor bering.

8. Commit va PR:

bash
git add assets/js/fayl.js assets/js/paket.js assets/js/asosiy.js \
  assets/js/render.js index.html jsconfig.json tekshiruv/paket.test.js
git commit -m "feat: eksport .json fayl qilib yuklab olinadi" \
  -m "Blob, createObjectURL, revokeObjectURL."
git push -u origin feature/eksport-fayl
gh pr create --fill
gh pr merge --merge

Commit sarlavhasi 72 belgidan oshmasin (Git qoidasi) — shuning uchun texnik tafsilot ikkinchi -m ga, tanaga ko'chdi.

9. Real ishda

  • Eksport tugmalari. Hisobotni CSV/Excel'ga, chekni PDF'ga, sozlamalarni JSON'ga — ko'p ilovalarda bu serversiz, aynan shu retsept bilan qilinadi. SheetJS kabi kutubxonalar haqiqiy .xlsx yasaydi, lekin oxirida baribir Blob + <a download>.
  • Rasm oldindan ko'rish. Telegram Web, Instagram — rasm tanlanganda u darhol ko'rinadi. Bu obyekt URL: fayl hali serverga ketmagan.
  • fetch → blob(). Serverdan kelgan rasm yoki PDF ni response.blob() bilan olib, obyekt URL bilan ko'rsatish (fetch asoslari).
  • Intervyu. "Blob va File farqi?", "createObjectURL dan keyin nima qilish kerak?", "Serversiz fayl qanday yuklab beriladi?" — frontend intervyularida uchraydi.

Xulosa

  • Blob — hajmi va turi bor baytlar bo'lagi; File — nomi va sanasi qo'shilgan Blob.
  • input.files — FileList (massiv emas); type kengaytmadan taxmin qilinadi, size — baytlarda.
  • new Blob(qismlar, { type }) satr, typed array va Blob'lardan yig'adi; slice() nusxasiz kesadi.
  • URL.createObjectURL(blob) — blob: manzil; ish tugagach URL.revokeObjectURL().
  • Yuklab berish: Blob → obyekt URL → <a download> → click() → revoke.
  • Fayl nomini doim textContent bilan ko'rsating.

Keyingi dars: Faylni o'qish va oldindan ko'rish — Blob'ni "ochish": matn, baytlar va rasm sifatida o'qish, yuklashdan oldin hajm va turni tekshirish.

Manbalar

  • W3C: File API — w3.org/TR/FileAPI
  • MDN: Blob, File, FileList, URL.createObjectURL() — developer.mozilla.org
  • HTML Living Standard: "Downloading resources" (download atributi) — html.spec.whatwg.org
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript File va Blob: faylni yaratish, ko'rsatish va yuklab berish — IlmHamroh