Mundarija (25)
- Bu darsda
- 1. Nega bu kerak?
- 2. sharp bilan tanishuv
- 3. Resize strategiyalari
- 3.1 Beshta fit
- 3.2 Kattalashtirmaslik
- 4. Format: hajm va vaqt
- 5. EXIF: GPS va yo'nalish
- 5.1 Telefon rasmi ichida nima bor
- 5.2 Yo'nalish hisobi
- 6. Piksel bomba
- 6.1 Hujum
- 6.2 Himoya
- 7. Blurhash va LQIP
- 8. Tezlik va navbat
- 9. Hujumchi nigohi
- 10. Ko'p uchraydigan xatolar
- 11. Mashqlar
- 1-mashq (oson): bo'shliqlarni to'ldiring
- 2-mashq (o'rta): ko'rinishlar rejasi
- 3-mashq (qiyin): kirish tekshiruvi va yo'nalish
- 12. Loyiha qadami: rasm.ts moduli
- 13. Real ishda
- Xulosa
- Manbalar
Rasmlarni qayta ishlash: sharp bilan o'lcham, WebP va EXIF tozalash
Qisqacha: Telefon rasmi 3–5 MB va 4000 piksel — ro'yxatda 160 piksellik kvadrat uchun bu isrof. sharp (libvips asosidagi kutubxona) yuklangan rasmdan kichik ko'rinishlar (thumbnail) yasaydi, WebP'ga o'tkazadi va metama'lumotni tashlaydi. Kanon o'lchovida 1280 px rasm JPEG'da 42.8 KB, WebP'da 20.6 KB. Ikki tuzoq:
rotate()siz telefon rasmi yonboshlab qoladi,limitInputPixelssiz 642 KB lik fayl ochilganda 644 MB xotira yeydi.
Bu darsda
cover,contain,fill,inside,outsidestrategiyalarini o'lcham hisobi bilan farqlaysiz.- JPEG, WebP, AVIF va PNG hajmi hamda tezligini kanon o'lchovi bilan solishtirasiz.
- EXIF/GPS tozalash va yo'nalish (orientation) tuzog'ini tushunasiz.
- Piksel bomba hujumini
limitInputPixelsbilan to'sasiz. - Blurhash va LQIP nimaligini, og'ir ishni nega navbatga berish kerakligini bilasiz.
Oldin bilishingiz kerak: Fayl yuklash xavfsizligi, Fayl metama'lumotini DB'da boshqarish, libuv thread pool va OS asinxron I/O, Buffer va binar ma'lumot.
Oldingi darsda fayl bazaga tayyor holatda yozildi. Rasm bo'lsa, unga yana bir qadam kerak: ko'rinishlar.
1. Nega bu kerak?
Malika vazifaga telefondan 4 MB lik foto biriktirdi. Ro'yxat sahifasida 20 ta shunday vazifa — sahifa 80 MB yuklaydi. Telefon interneti uchun bu halokat.
Yechim — yuklashdan keyin serverda bir marta ko'rinishlar (variants) yasash:
kichik— 160×160 kvadrat, ro'yxat uchun;orta— eni yoki bo'yi 1280 gacha, ochilganda;- asl fayl — faqat "yuklab olish" tugmasi uchun.
Yana ikki foyda bor. Maxfiylik: telefon rasmida GPS koordinatalari bor — Malika uyining manzili. Qayta kodlangan rasmda ular yo'q. Xavfsizlik: qayta kodlash faqat piksellarni oladi — Fayl yuklash xavfsizligi darsidagi GIF+JS polyglot ichidagi skript yangi faylga o'tmaydi.
2. sharp bilan tanishuv
sharp — C kutubxonasi libvips ustidagi Node qatlami. Kanon versiyalari: sharp 0.35.5, libvips 8.18.7.
npm i sharp@0.35.5O'rnatishda tayyor binar (Windows, Linux, macOS uchun) yuklanadi — kompilyator kerak emas. Kanon mashinasida:
libvips 8.18.7 | concurrency 16 | SIMD true
manba: 3840×2160 JPEG, 1009 KBAPI — zanjir (chain): kirish → amallar → format → chiqish.
import sharp from "sharp";
const { data, info } = await sharp(kirishBuferi)
.rotate()
.resize(1280, 1280, { fit: "inside", withoutEnlargement: true })
.webp({ quality: 80 })
.toBuffer({ resolveWithObject: true });
console.log(info.width, info.height, data.length);Zanjir dangasa: toBuffer() chaqirilgunga qadar hech narsa ishlamaydi. libvips rasmni bo'laklab (oqim kabi) qayta ishlaydi — butun rasmni xotiraga ochmaydi, shuning uchun tez.
3. Resize strategiyalari
3.1 Beshta fit
400×400 ga sig'dirish kerak, manba 3840×2160 (16:9). Kanon natijasi:
cover 400×400
contain 400×400
fill 400×400
inside 400×225
outside 711×400
faqat width 1280: 1280×720
100×80 → 400 withoutEnlargement: 100×80O'lchamlar qayerdan kelishini toza JS bilan hisoblaymiz:
function olcham(eni, boyi, w, h, fit) {
if (fit === "cover" || fit === "contain" || fit === "fill") {
return `${w}×${h}`;
}
const kx = w / eni;
const ky = h / boyi;
const k = fit === "inside" ? Math.min(kx, ky) : Math.max(kx, ky);
return `${Math.round(eni * k)}×${Math.round(boyi * k)}`;
}
for (const fit of ["cover", "contain", "fill", "inside", "outside"]) {
console.log(fit.padEnd(8), olcham(3840, 2160, 400, 400, fit));
}Konsolda:
cover 400×400
contain 400×400
fill 400×400
inside 400×225
outside 711×400Kanon bilan aynan bir xil. Har biri nima qiladi:
fit |
Natija | Rasm bilan nima bo'ladi | Qachon |
|---|---|---|---|
cover |
aniq 400×400 | chetlari kesiladi | avatar, ro'yxat kvadrati |
contain |
aniq 400×400 | to'liq, bo'sh joy fon bilan | mahsulot rasmi oq fonda |
fill |
aniq 400×400 | cho'ziladi (buziladi) | deyarli hech qachon |
inside |
≤ 400×400 | to'liq, nisbat saqlanadi | "ochilganda" ko'rinish |
outside |
≥ 400×400 | to'liq, nisbat saqlanadi | keyin kesish uchun |
inside uchun kichik koeffitsient (min) olinadi — rasm qutiga sig'adi. outside uchun katta (max) — qutini to'liq qoplaydi.
3.2 Kattalashtirmaslik
Oxirgi kanon qatoriga qarang: 100×80 rasm withoutEnlargement: true bilan 400 ga kattalashtirilmadi. Kichik rasmni kattalashtirish sifatni oshirmaydi, faqat hajmni. Kanon modulida ikkala ko'rinish shu bayroq bilan.
Tekshirib ko'ring: Avatar uchun foydalanuvchi yuzi rasmning chap chetida.
covermarkazdan kesadi — yuz qisman kesiladi. Nima qilsa bo'ladi?
Javob
sharp'da position parametri bor: "left", "top" kabi yo'nalishlar yoki sharp.strategy.attention (eng "qiziq" qismni — yorqin va kontrastli joyni — saqlaydi). Eng ishonchli yo'l — foydalanuvchiga brauzerda kesish ramkasini berish va koordinatalarni serverga yuborish (Rasm UI komponentlari).
4. Format: hajm va vaqt
Kanon 3840×2160 fotoni eni 1280 ga kichraytirib, har formatda 10 marta yasadi (mediana):
jpeg q80 (mozjpeg) 35.8 KB 89 ms
jpeg q80 42.8 KB 50 ms
webp q80 20.6 KB 110 ms
avif q50 11.6 KB 305 ms
avif q50 effort 2 14.6 KB 115 ms
png 892.1 KB 90 ms
thumbnail 160×160 webp: mediana 29.6 msXulosalar:
- PNG — foto uchun yaroqsiz. 892 KB — JPEG'dan 20 barobar katta. PNG — skrinshot, logotip, shaffof fon uchun.
- WebP — JPEG'ning yarmi, ikki barobar sekinroq. Bugun hamma zamonaviy brauzer ko'rsatadi. Kanon moduli tanlovi.
- AVIF — eng kichik, lekin 6 barobar sekin.
effort: 2(kamroq urinish) bilan vaqt WebP'ga yaqinlashdi, hajm biroz oshdi. - mozjpeg — o'sha JPEG, yaxshiroq siqish, 1.8 barobar sekin. Faqat JPEG kerak bo'lsa.
Vaqt — bitta rasm uchun, bitta mashinada (i5-12500H). Yuklashda bir marta qilinadi, minglab ko'rishda tejaladi — shuning uchun sekinroq, lekin kichikroq format odatda foydali.
Maslahat: Brauzerga format tanlashni bering:
<picture>ichida<source type="image/avif">, keyin WebP, oxirida JPEG (Responsive rasmlar). Server bir nechta format saqlaydi — lekin avval bitta (WebP) bilan boshlang.
5. EXIF: GPS va yo'nalish
5.1 Telefon rasmi ichida nima bor
EXIF — rasm ichidagi metama'lumot: kamera modeli, sana, GPS koordinatalari va Orientation (yo'nalish). Kanon 800×450 rasmga GPS va orientation 6 (90° burilgan) yozib sinadi:
manba: exif 332 bayt, GPS satri bor: true | orientation 6 | o'lcham 800×450
sukut (metadata tashlanadi): exif yo'q | orientation yo'q | o'lcham 400×225 ← rotate() SIZ yo'nalish yo'qoldi (yonboshlab)
rotate() bilan: 400×711 exif yo'q
keepMetadata(): exif 332 bayt — GPS ham SAQLANIB qoladi (XATO tanlov)Uchta holat:
- Sukut. sharp chiqishga metama'lumot yozmaydi — GPS tashlandi. Yaxshi. Lekin
Orientationham tashlandi, piksellar esa burilmagan. Telefon rasmni "yonboshlab" saqlab, "90° bur" deb belgilagan edi; belgi yo'qoldi — rasm yonboshlab ko'rinadi. rotate()(argumentsiz) — piksellarni EXIF bo'yicha haqiqatan buradi, keyin belgi kerak emas. 400×711 — tik rasm, to'g'ri.keepMetadata()— hammasini saqlaydi, GPS bilan. Foydalanuvchi rasmlari uchun xato tanlov.
5.2 Yo'nalish hisobi
EXIF Orientation 1–8 qiymat oladi; 5–8 — eni va bo'yi almashadi (90° yoki 270°):
const BURILGAN = new Set([5, 6, 7, 8]);
function korinadi(eni, boyi, orientation = 1) {
return BURILGAN.has(orientation)
? { eni: boyi, boyi: eni } : { eni, boyi };
}
const k = korinadi(800, 450, 6);
console.log(k);
console.log(400, "×", Math.round(400 * k.boyi / k.eni));Konsolda:
{ eni: 450, boyi: 800 }
400 × 711Kanonning rotate() bilan: 400×711 qatori aynan shu hisob.
Diqqat:
rotate()ni zanjirning boshida chaqiring —resizedan oldin. Aks holda o'lcham burilmagan rasm bo'yicha hisoblanadi.
6. Piksel bomba
6.1 Hujum
PNG siqiladi: bir xil rangli katta maydon juda kichik faylga sig'adi. Ochilganda esa har piksel xotiraga yoziladi. Kanon 15000×15000 qora PNG yasadi:
15000×15000 PNG: 642 KB fayl, ochilsa 644 MB xotira
sukut (268 mln px): o'tdi
40 mln limit: Error: Input image exceeds pixel limit
metadata() o'zi: 15000×15000, 0.5 ms (piksellarni ochmaydi)Hisob:
const MB = 1024 * 1024;
const piksel = 15000 * 15000;
console.log(piksel.toLocaleString("en-US"), "piksel");
console.log((piksel * 3 / MB).toFixed(0), "MB (RGB, 3 bayt)");
console.log(piksel > 268402689, piksel > 40_000_000);
console.log(Math.floor(Math.sqrt(40_000_000)));Konsolda:
225,000,000 piksel
644 MB (RGB, 3 bayt)
false true
6324- 642 KB fayl → 644 MB xotira: 1000 barobar. Bir nechta parallel yuklash — server xotirasi tugaydi (DoS va resurs tugashidan himoya).
- sharp sukut limiti 268 402 689 piksel (16 383²) — bu bomba undan kichik, shuning uchun o'tdi.
- 40 mln limit — taxminan 6 324×6 324. Telefon kamerasi (12–50 MP) uchun yetarli. 50 MP kamera bo'lsa — limitni oshiring.
6.2 Himoya
Ikki qatlam:
metadata()— sarlavhadan o'lchamni oladi, piksellarni ochmaydi (0.5 ms). Avval shu bilan tekshiring.sharp(kirish, { limitInputPixels: 40_000_000 })— har ochishda limit; oshsaInput image exceeds pixel limit("kirish rasmi piksel limitidan oshdi").
7. Blurhash va LQIP
Rasm yuklanguncha joyida nima turadi? Bo'sh oq kvadrat sahifani "sakratadi". Ikki texnika:
blurhash: Lt3J]@kIf1a*kJf4kDkEayfOkCfN (28 belgi, 3.9 ms, kirish 32×18)
LQIP 16 px webp data URL: 143 belgi- Blurhash — rasmning rang tarqalishini 20–30 belgilik satrga siqadi (
blurhashpaketi, 2.0.5). Bazada saqlanadi, brauzer uni xira rasmga "ochadi". Kirish kichik bo'lishi kerak (kanon 32×18) — aks holda sekin. - LQIP (Low Quality Image Placeholder — past sifatli o'rinbosar) — 16 px WebP,
data:URL sifatida HTML'ga to'g'ridan qo'yiladi: 143 belgi.
Ikkalasi ham API javobiga bir necha o'nlab bayt qo'shadi, sahifa esa darhol "rasmli" ko'rinadi.
8. Tezlik va navbat
16 parallel: 677 ms; ketma-ket 1 ta ~ yuqoridagi webp vaqtiKanon chiqishidagi izoh qatorini matnga ko'chirdik: sharp ishni libuv thread pool'iga beradi (UV_THREADPOOL_SIZE sukutda 4) — event loop bloklanmaydi (libuv thread pool va OS asinxron I/O). 16 ta parallel ish 677 ms da tugadi. Lekin CPU bitta: rasm qayta ishlanayotganda API so'rovlari ham sekinlashadi. Shuning uchun haqiqiy loyihada:
- yuklash javobi darhol qaytadi ("rasm qayta ishlanmoqda");
- ko'rinishlarni navbatdagi vazifa (job) yasaydi — boshqa jarayonda: worker (navbatdan ish olib bajaradigan alohida jarayon; BullMQ asoslari darsida quramiz);
- tayyor bo'lgach bazada
korinishlaryangilanadi.
flowchart LR
A[Yuklash: tayyor] --> Q[(Navbat)]
Q --> W[Worker: sharp]
W -->|"metadata() + limit"| X{Yaroqlimi?}
X -->|ha| R[rotate, resize, WebP]
R --> S[(Ombor: kichik, orta)]
R --> D[(Baza: o'lchamlar)]
X -->|yo'q| E[RasmXato, holat yozildi]9. Hujumchi nigohi
- Piksel bomba. Kichik fayl — ulkan xotira. Himoya:
metadata()+limitInputPixels. - GPS sizishi. Asl rasm boshqa foydalanuvchilarga ko'rsatiladi — uy manzili bilan. Himoya: faqat qayta kodlangan ko'rinishlarni ko'rsating; asl faylni faqat egasiga.
- Polyglot. Rasm ham, skript ham bo'lgan fayl. Himoya: qayta kodlash — yangi fayl faqat piksellardan.
- Buzilgan fayl. Ataylab buzilgan sarlavha kutubxona xatosini chaqiradi. Himoya: xatoni ushlab,
RasmXatoga aylantiring; jarayon yiqilmasin. - Ko'p ish. 1 000 ta rasmni bir vaqtda yuklash — CPU band. Himoya: navbat, parallellik limiti, Rate limiting.
10. Ko'p uchraydigan xatolar
| Xato | Sabab | Davosi |
|---|---|---|
| Rasm yonboshlab chiqadi | rotate() yo'q |
zanjir boshida .rotate() |
| GPS ko'rinishda qoldi | keepMetadata() / withMetadata() |
sukut (tashlanadi) |
| Kichik rasm xira, katta | kattalashtirildi | withoutEnlargement: true |
| Rasm cho'zilgan | fit: "fill" |
cover yoki inside |
Input image exceeds pixel limit |
limit | foydalanuvchiga "rasm juda katta" |
| Server xotirasi to'ldi | sukut limit 268 mln | limitInputPixels |
| API sekinlashdi | sharp so'rov ichida | navbat va worker |
TS: sharp.Metadata topilmadi |
sharp 0.35 ESM tiplari | import { type Metadata } from "sharp" |
11. Mashqlar
Mashqlar kurs/mashqlar/29/07-sharp/ da.
1-mashq (oson): bo'shliqlarni to'ldiring
- Nisbatni saqlab, qutiga to'liq sig'diradigan
fit— - Aniq o'lchamga, chetlarni kesib —
- EXIF yo'nalishi bo'yicha burish metodi —
- Piksel bomba himoyasi parametri —
- Kanon o'lchovida 1280 px foto uchun eng kichik format —
Yechim
- inside — natija ≤ quti.
- cover — natija aynan quti.
- rotate — argumentsiz, zanjir boshida.
- limitInputPixels — kirishdagi piksellar soni.
- AVIF — 11.6 KB, lekin eng sekin (305 ms).
2-mashq (o'rta): ko'rinishlar rejasi
Kanon modulida ikki ko'rinish bor: kichik 160 cover va orta 1280 inside, ikkalasi withoutEnlargement. reja(eni, boyi) funksiyasini yozing: har ko'rinish uchun "nom E×B" satrini qaytarsin. Rasm qutidan kichik bo'lsa — o'lcham o'zgarmaydi. 3000×2000, 100×50 va 3840×2160 bilan sinang (birinchi ikkitasi — kanon testidagi qiymatlar). Ishora: «Beshta fit» bo'limidagi olcham mantiqi.
Yechim
const KORINISHLAR = [
{ nom: "kichik", olcham: 160, fit: "cover" },
{ nom: "orta", olcham: 1280, fit: "inside" },
];
function reja(eni, boyi) {
return KORINISHLAR.map(({ nom, olcham, fit }) => {
if (eni <= olcham && boyi <= olcham) {
return `${nom} ${eni}×${boyi}`;
}
if (fit === "cover") {
const e = Math.min(eni, olcham);
return `${nom} ${e}×${Math.min(boyi, olcham)}`;
}
const k = Math.min(olcham / eni, olcham / boyi);
return `${nom} ${Math.round(eni * k)}×${Math.round(boyi * k)}`;
});
}
console.log(reja(3000, 2000));
console.log(reja(100, 50));
console.log(reja(3840, 2160));Konsolda:
[ 'kichik 160×160', 'orta 1280×853' ]
[ 'kichik 100×50', 'orta 100×50' ]
[ 'kichik 160×160', 'orta 1280×720' ]Birinchi ikki natija kanon testidagi [160, 160, 1280, 853] va [100, 50, 100, 50] bilan bir xil. Bu — model: chekka holatlarda (masalan 3000×100 cover) sharp boshqacha natija berishi mumkin; aniq javob — sharp'ning o'zi.
3-mashq (qiyin): kirish tekshiruvi va yo'nalish
tekshir({ eni, boyi, orientation }) funksiyasini yozing (bu — metadata() natijasi). O'lcham yo'q bo'lsa { xato: "o'lcham noma'lum" }, piksellar 40 mln dan ko'p bo'lsa { xato: "juda katta: E×B" }. Aks holda yo'nalishni hisobga olib (5–8 — eni va bo'yi almashadi), orta ko'rinish o'lchamini qaytarsin (1280 inside, kattalashtirmaslik). Kanon testidagi 400×200 orientation 6, 4000×3000 (1 va 8), 8000×8000 va 0×100 bilan sinang.
Yechim
const MAKS_PIKSEL = 40_000_000;
const BURILGAN = new Set([5, 6, 7, 8]);
function tekshir({ eni, boyi, orientation = 1 }) {
if (!eni || !boyi) return { xato: "o'lcham noma'lum" };
if (eni * boyi > MAKS_PIKSEL) {
return { xato: `juda katta: ${eni}×${boyi}` };
}
const burilgan = BURILGAN.has(orientation);
const [e, b] = burilgan ? [boyi, eni] : [eni, boyi];
const k = Math.min(1, 1280 / e, 1280 / b);
return { orta: `${Math.round(e * k)}×${Math.round(b * k)}` };
}
console.log(tekshir({ eni: 400, boyi: 200, orientation: 6 }));
console.log(tekshir({ eni: 4000, boyi: 3000, orientation: 1 }));
console.log(tekshir({ eni: 4000, boyi: 3000, orientation: 8 }));
console.log(tekshir({ eni: 8000, boyi: 8000 }));
console.log(tekshir({ eni: 0, boyi: 100 }));Konsolda:
{ orta: '200×400' }
{ orta: '1280×960' }
{ orta: '960×1280' }
{ xato: 'juda katta: 8000×8000' }
{ xato: "o'lcham noma'lum" }Math.min(1, …) — kattalashtirmaslik. Birinchi qator kanon testidagi "Orientation 6 → 200×400" bilan bir xil. Tartib muhim: limit tekshiruvi burishdan oldin — burish uchun piksellarni ochish kerak, bomba esa ochilmasligi kerak.
12. Loyiha qadami: rasm.ts moduli
Kanon moduli (telefon uchun bo'lingan, izohlar qisqartirildi, mazmuni kanondagidek):
// rasm.ts — yuklangan rasmdan ko'rinishlar: EXIF yo'nalishi, o'lcham,
// WebP, metama'lumot (GPS) tozalash, piksel bomba limiti.
// Qayta kodlash polyglot qo'shimcha baytlarini ham tashlaydi.
import sharp, { type Metadata } from "sharp";
export const MAKS_PIKSEL = 40_000_000; // ~ 6 300 × 6 300
export type Korinish = {
nom: "kichik" | "orta"; eni: number; boyi: number;
format: "webp"; bayt: Buffer;
};
export const KORINISHLAR = [
{ nom: "kichik", olcham: 160, fit: "cover" }, // ro'yxat kvadrati
{ nom: "orta", olcham: 1280, fit: "inside" }, // ochilganda
] as const;
export class RasmXato extends Error {
constructor(xabar: string) {
super(xabar);
this.name = "RasmXato";
}
}
export async function korinishlar(
kirish: Buffer,
): Promise<Korinish[]> {
let ma: Metadata;
try {
ma = await sharp(kirish, { limitInputPixels: MAKS_PIKSEL })
.metadata();
} catch (e) {
throw new RasmXato(`rasm o'qilmadi: ${
e instanceof Error ? e.message : String(e)}`);
}
if (!ma.width || !ma.height) throw new RasmXato("o'lcham noma'lum");
if (ma.width * ma.height > MAKS_PIKSEL) {
throw new RasmXato(
`juda katta: ${String(ma.width)}×${String(ma.height)}`);
}
const natija: Korinish[] = [];
for (const k of KORINISHLAR) {
const { data, info } = await sharp(kirish,
{ limitInputPixels: MAKS_PIKSEL })
.rotate() // EXIF Orientation bo'yicha burish
.resize(k.olcham, k.olcham,
{ fit: k.fit, withoutEnlargement: true })
.toColourspace("srgb")
.webp({ quality: 80, effort: 4 })
.toBuffer({ resolveWithObject: true });
natija.push({
nom: k.nom, eni: info.width, boyi: info.height,
format: "webp", bayt: data,
});
}
return natija;
}metadata()— avval, arzon; limit ikki marta (sharp va o'z hisobimiz).toColourspace("srgb")— telefon rasmlarida boshqa rang profillari (Display P3) bo'ladi; brauzerlar uchun sRGB eng xavfsiz.- Metama'lumot yozilmaydi — sharp sukuti.
- Tip topilmasi (kanon): sharp 0.35 ESM tiplarida
sharp.Metadatanamespace yo'q —import { type Metadata } from "sharp".
Testlar (test/fayl-rasm.test.ts) — 4 ta, tashqi xizmat kerak emas. Ularni o'zimiz yurgizdik (biz ishga tushirdik: Node 24.21.0, sharp 0.35.5):
✔ ko'rinishlar: kichik 160×160 cover, o'rta nisbat saqlanadi, WebP (195.2658ms)
✔ kichik rasm kattalashtirilmaydi (8.6427ms)
✔ EXIF Orientation 6 → burilgan; GPS va EXIF natijada yo'q (34.4172ms)
✔ piksel bomba va buzilgan fayl — RasmXato (330.7948ms)
ℹ tests 4
ℹ suites 0
ℹ pass 4
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 746.725Hammasi ✔, o'tkazilgan (﹣) yo'q. Vaqtlar har ishga tushishda boshqacha.
Commit xabari (modul uchun):
git commit -m "feat(fayl): korinishlar — sharp, WebP, EXIF, limit"vazifalar-api ga ulash 28-qism tugagach va navbat (BullMQ asoslari) bilan: rasm tasdiqlangach vazifa navbatga qo'yiladi.
13. Real ishda
- Ijtimoiy tarmoqlar va marketpleyslar odatda har yuklangan rasmni bir nechta o'lchamda saqlaydi va metama'lumotini tozalaydi.
- CDN transformatsiyalari (Cloudflare Images, imgix) — o'lchamni havola parametri bilan so'raysiz, server o'zi yasaydi. Kichik loyihada sharp + navbat arzonroq.
- Next.js
<Image>ichida ham sharp ishlaydi (next/image). - Intervyuda: "Foydalanuvchi rasmini qanday saqlaysiz?", "EXIF nima va nega xavfli?", "Rasm qayta ishlashni qayerda qilasiz — so'rov ichidami?"
Xulosa
- sharp — libvips ustida tez, oqimli rasm qayta ishlash; zanjir
toBuffer()da bajariladi. cover— aniq kvadrat (kesib),inside— nisbat bilan qutiga;withoutEnlargement— kattalashtirmaslik.- WebP q80 — JPEG'ning yarmi (20.6 vs 42.8 KB), AVIF — eng kichik va eng sekin; PNG — foto uchun emas.
- Metama'lumot sukutda tashlanadi;
rotate()siz yo'nalish yo'qoladi;keepMetadata()GPS'ni saqlaydi. - 642 KB → 644 MB:
metadata()+limitInputPixels(40 mln). - Og'ir ish — navbatda, worker'da.
Keyingi dars: Video va audio: ffmpeg — Node'dan tashqi dasturni xavfsiz ishga tushirish, thumbnail, HLS va timeout.
Manbalar
- sharp hujjati —
resize(fit, withoutEnlargement),rotate,limitInputPixels, metama'lumot: https://sharp.pixelplumbing.com/api-resize va https://sharp.pixelplumbing.com/api-constructor - libvips: https://www.libvips.org/
- EXIF Orientation qiymatlari (CIPA Exif standarti): https://www.cipa.jp/e/std/std-sec.html
- Blurhash: https://blurha.sh/
- Kanon tajribasi (IlmHamroh, 2026-10-27): sharp 0.35.5, libvips 8.18.7, blurhash 2.0.5, Node 24.21.0, i5-12500H, Windows 11; manba — Windows bilan keladigan 3840×2160 foto. Testlar, o'lcham hisoblari va mashq yechimlari — biz ishga tushirdik (2026-10-28).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!