WisarWisar
Dasturlash kitobi/8-QISM — NestJS28 daqiqa

8.24-bob: Rasm/media qayta ishlash — sharp

8-QISM — NestJS (chuqur) · 24-mavzu · Amaliy real mavzu


1. Kirish va motivatsiya

5.11 va 8.8'da fayl yuklashni ko'rdik — endi yuklangan rasmni qayta ishlash ni o'rganamiz. Bu — har rasm bilan ishlaydigan loyihada (e-commerce mahsulot rasmi, avatar, e'lon, galereya) kerak bo'ladigan real mavzu. Muammo: foydalanuvchi 8 megabaytli, 4000×3000 piksel rasm yuklaydi — uni shundayligicha saqlasangiz: server joyi to'ladi, sayt sekin yuklanadi (mobil internetda dahshat), turli joyda turli o'lcham kerak (ro'yxatda kichik, sahifada katta). Yechim — rasmni avtomatik qayta ishlash: o'lchamni kichraytirish, siqish, format o'zgartirish, thumbnail yaratish.

Node.js'da rasm qayta ishlashning standarti — sharp (eng tez, eng ko'p ishlatiladigan kutubxona — libvips ustida, C darajasida tez). U: resize (o'lcham), format konversiya (JPEGWebP — 30% kichikroq), siqish (sifat), thumbnail (kichik nusxa), watermark (suv belgisi), crop (kesish), metadata (EXIF o'qish/tozalash). Bularning hammasi — millisekundlar ichida, kam xotira (streaming). Bu — sayt tezligi (Core Web Vitals — SEO — 13) va saqlash xarajatining muhim qismi.

Bu bob: nega rasm qayta ishlash, sharp asoslari, resize (o'lcham strategiyalari), format va siqish (WebP/AVIF — zamonaviy), bir necha o'lcham (responsive — thumbnail/medium/large), watermark, EXIF/metadata (xavfsizlik — joylashuv tozalash), avatar/crop, upload bilan integratsiya (5.11 davomi), va fonda qayta ishlash (navbat — 8.22). Bu bob 5.11/8.8 (yuklash), 8.22 (navbat), 13 (Next.js Image) bilan bog'liq. Rasm qayta ishlash — sayt tezligi va sifatining muhim qismi.

O'xshatish: rasm qayta ishlash — fotosurat studiyasi. Mijoz xom, katta foto (RAW) keltiradi (8MB original). Studiya: kerakli o'lchamga kesadi/kichraytiradi (resize — pasport uchun kichik, devor uchun katta), sifatni saqlab siqadi (optimize — fayl yengil), turli nusxa tayyorlaydi (thumbnail — albom uchun, katta — chop etish uchun), logotip qo'shadi (watermark), va shaxsiy ma'lumotni (foto qayerda olingani — GPS — EXIF) tozalaydi. Backend — raqamli foto studiya: bitta yuklangan rasmdan barcha kerakli, optimallashtirilgan nusxalarni avtomatik yaratadi.

Nega muhim?

  • Sayt tezligi — kichik/optimallashgan rasm (mobil, SEO — 13).
  • Saqlash xarajati — 8MB200KB (40x kam joy).
  • Turli o'lcham — responsive (thumbnail, medium, large).
  • Har rasm loyihada — mahsulot, avatar, e'lon, galereya.

2. Nazariya — chuqur tushuntirish

2.1. Nega rasm qayta ishlash

text
  MUAMMO (xom rasm shundayligicha):
   8MB, 4000×3000  server joyi to'ladi, sayt sekin (mobil — dahshat)
   Turli joyda turli o'lcham (ro'yxat — kichik, sahifa — katta)
   Eski format (JPEG — WebP'dan 30% katta)
   EXIF — foydalanuvchi joylashuvi (maxfiylik!)

  YECHIM (sharp bilan):
   Resize (kerakli o'lcham), optimize (siqish — 8MB  200KB)
   Format (WebP/AVIF — zamonaviy, kichik)
   Bir necha o'lcham (thumbnail/medium/large)
   EXIF tozalash (maxfiylik)

Nega kerak: xom rasm — katta (server/tezlik muammosi), bir o'lcham (turli joyga mos emas), eski format (katta), EXIF (maxfiylik). sharp hal qiladi: resize, optimize (8MB200KB), format (WebP), bir necha o'lcham, EXIF tozalash. Sayt tezligi (mobil, SEO — 13) va xarajat (saqlash) — to'g'ridan rasmga bog'liq. Optimallashtirilmagan rasm — eng keng tezlik muammosi.

2.2. sharp asoslari

typescript
import * as sharp from "sharp";

// sharp — zanjir (fluent — 9.2 Builder): o'qi  o'zgartir  chiqar
const natija = await sharp(inputBuffer)               // kirish (buffer/fayl)
  .resize(800, 600)                                   // o'lcham (2.3)
  .webp({ quality: 80 })                              // format + sifat (2.4)
  .toBuffer();                                        // chiqish (buffer/fayl)

// Yoki faylga
await sharp("input.jpg").resize(800).toFile("output.webp");

sharp asoslari — zanjir (fluent — Builder 9.2): sharp(input) (buffer/fayl/stream) .resize()/.webp()/... (o'zgartirishlar) .toBuffer()/.toFile() (chiqish). libvips ustida (C — eng tez, kam xotira). Async (Promise). Kirish — memoryStorage buffer 5.11-bob yoki fayl/stream. Chiqish — buffer (S3'ga — 5.11) yoki fayl. Eng tez Node.js rasm kutubxonasi (production standart).

2.3. Resize (o'lcham strategiyalari)

typescript
// Resize rejimlari (fit — belgilangan o'lchamga qanday sig'dirish)
sharp(input).resize(300, 300, { fit: "cover" });      // to'ldirish (proporsiya saqlab, ortiqchani kesadi — avatar)
sharp(input).resize(300, 300, { fit: "contain" });    // ichiga (proporsiya saqlab, atrofga bo'sh joy — fon bilan)
sharp(input).resize(300, 300, { fit: "inside" });     // proporsiya saqlab, ichiga — hech biri oshmasin (default)
sharp(input).resize(300, 300, { fit: "outside" });    // proporsiya saqlab, hech biri kichik bo'lmasin (kamida qoplaydi)
sharp(input).resize(300, 300, { fit: "fill" });       //  aniq 300×300 — proporsiya buziladi (cho'ziladi)
sharp(input).resize(800, null);                        // faqat kenglik (balandlik proporsional — avtomatik)
sharp(input).resize(null, 600);                        // faqat balandlik (kenglik avtomatik)
sharp(input).resize(800, null, { withoutEnlargement: true });   // kichikni kattalashtirmaslik

// position — cover/contain'da qaysi qism qoladi / qayerga tekislanadi
sharp(input).resize(300, 300, { fit: "cover", position: "top" });        // yuqorini saqlab kesadi (portret — yuz)
sharp(input).resize(300, 300, { fit: "cover", position: "attention" });  //  diqqat markazi (yuz/kontrast) avtomatik
sharp(input).resize(300, 300, { fit: "cover", position: "entropy" });    // eng ko'p detalli qismni saqlaydi

// background — contain'dagi bo'sh joy yoki shaffof PNG'ni JPEG fonini to'ldirish
sharp(input).resize(300, 300, { fit: "contain", background: "#ffffff" });          // oq fon
sharp(input).resize(300, 300, { fit: "contain", background: { r: 0, g: 0, b: 0, alpha: 0 } });  // shaffof (PNG/WebP)

Resize strategiyalari: fit — belgilangan quti ichiga qanday sig'dirish: cover (to'ldirish — proporsiya saqlab ortiqchani kesadi — avatar, thumbnail), contain (ichiga — proporsiya saqlab, qolgan joy background bilan to'ladi), inside (proporsiya saqlab, tomonlarning hech biri berilgandan oshmaydi — default), outside (proporsiya saqlab, tomonlarning hech biri kichik bo'lmaydi — kamida qutini qoplaydi), fill ( aniq o'lchamga majburiy cho'zish — proporsiya buziladi, kamdan-kam kerak). Faqat bitta o'lcham berilsa (resize(800, null) yoki resize(null, 600)) — ikkinchisi proporsional hisoblanadi. position (cover/contain uchun) — qaysi qism kesilib qolishini boshqaradi: centre (default), top/left/... (chekka), yoki aqlli variantlar attention (kontrast/yuz eng ko'p bo'lgan joyni saqlaydi) va entropy (eng ko'p ma'lumotli qism). background — contain'dagi bo'sh joyni yoki shaffof rasmni matnsiz formatga (JPEG) o'tkazishda fonni to'ldiradi (alpha: 0 — shaffof saqlash). withoutEnlargement (kichik rasmni kattalashtirmaslik — sifat buzilmasin). To'g'ri fit — maqsadga bog'liq (avatarcover+attention; mahsulotinside). Eng ko'p ishlatiladigan amal.

2.4. Format va siqish (WebP/AVIF)

text
  FORMATLAR (zamonaviy  kichik):
  - JPEG: keng, lekin katta (eski)
  - WebP: 25-35% kichik (JPEG'dan) — zamonaviy, keng qo'llab-quvvatlanadi
  - AVIF: 50% kichik — eng yangi (ba'zi eski brauzer qo'llamaydi)
  - PNG: shaffoflik (lekin katta — faqat kerak bo'lganda)

  SIQISH (quality 0-100):
  .webp({ quality: 80 })   sifat/hajm balansi (80 — odatda yaxshi)

Format va siqish: WebP (JPEG'dan 25-35% kichik — zamonaviy, keng) — default tanlov; AVIF (50% kichik — eng yangi, lekin eski brauzer cheklov); JPEG (eski, keng); PNG (shaffoflik — katta). Siqish (quality 0-100 — 80 odatda yaxshi balans — ko'z farqlamaydi, lekin hajm yarmi). WebP'ga o'tish — eng oson tezlik yutug'i (30% kichik, sifat bir xil). Format + sifat — hajmni keskin kamaytiradi.

AVIF vs WebP taqqoslash (bir xil fotosuratni turli formatga o'tkazganda taxminiy hajm/xususiyat):

Xususiyat JPEG WebP AVIF
Nisbiy hajm (bir xil sifatda) 100% (asos) ~65-75% ~45-55%
Shaffoflik (alpha)
Kodlash tezligi tez tez sekin (CPU og'ir — effort)
Brauzer qo'llovi (2026) universal deyarli universal zamonaviy (eski Safari/IE yo'q)
Animatsiya (cheklangan)
Eng mos joy fallback, keng moslik default zamonaviy maksimal siqish, banner/hero

Amaliy tanlov: default sifatida WebP (siqish + moslik balansi), maksimal tejash kerak bo'lganda (hero rasm, ko'p trafik) AVIF + WebP fallback (<picture> yoki Accept header — Misol 8). AVIF kodlash CPU'ni ko'p yeydi (effort past qo'ying yoki fonda — 2.9). Eski brauzer uchun JPEG fallback saqlab qoling.

2.5. Bir necha o'lcham (responsive)

typescript
// Bir rasmdan — bir necha o'lcham (turli joyga)
const olchamlar = [
  { nom: "thumbnail", en: 150 },                      // ro'yxat, avatar
  { nom: "medium", en: 600 },                         // sahifa
  { nom: "large", en: 1200 },                         // to'liq ko'rish
];

async hammaOlchamYarat(input: Buffer, id: string) {
  const natijalar: Record<string, string> = {};
  for (const o of olchamlar) {
    const buffer = await sharp(input).resize(o.en, null, { withoutEnlargement: true })
      .webp({ quality: 80 }).toBuffer();
    natijalar[o.nom] = await this.s3.yukla(buffer, `${id}-${o.nom}.webp`);   // S3 (5.11)
  }
  return natijalar;                                   // { thumbnail: url, medium: url, large: url }
}

Bir necha o'lcham (responsive — muhim amaliyot): bir yuklangan rasmdan bir necha o'lcham (thumbnail — ro'yxat, medium — sahifa, large — to'liq). Frontend kerakli o'lchamni ishlatadi (mobil — kichik, desktop — katta — srcset — 13). DB'ga barcha URL saqlanadi (5.11: 2.12). Tezlik: ro'yxatda 1200px rasm yuklash — behuda (150px yetadi). Responsive rasm — sayt tezligining kaliti.

2.6. Watermark (suv belgisi)

typescript
// Watermark — logotip/matn qo'shish (mualliflik, brending)
async watermark(input: Buffer): Promise<Buffer> {
  const logo = await sharp("logo.png").resize(150).png().toBuffer();
  return sharp(input)
    .composite([{                                     // ustiga qo'yish
      input: logo,
      gravity: "southeast",                           // o'ng past burchak
      blend: "over",
    }])
    .toBuffer();
}
// Matn watermark — SVG orqali (matnni rasmga aylantirib composite)

Watermark (suv belgisi) — rasmga logotip/matn qo'shish (mualliflik himoyasi, brending). composite (ustiga qo'yish) + gravity (joylashuv — burchak/markaz) + blend (aralashtirish). Logo (PNG — shaffof) yoki matn (SVGrasm). E'lon, mahsulot rasmiga brending. Foydali, lekin majburiy emas (UX — ortiqcha watermark bezovta qiladi).

2.7. EXIF va metadata (xavfsizlik)

typescript
// EXIF — rasm metadata (kamera, SANA, GPS JOYLASHUV — maxfiylik!)
const metadata = await sharp(input).metadata();       // { width, height, format, exif }

//  EXIF tozalash (foydalanuvchi joylashuvi oshkor bo'lmasin — 14)
const tozalangan = await sharp(input)
  .rotate()                                           // EXIF orientation'ga qarab to'g'rilash
  .withMetadata({ exif: {} })                         // EXIF tozalash (yoki .toBuffer — default tozalaydi)
  .toBuffer();

EXIF/metadata (xavfsizlik — ko'p unutiladi): rasm EXIF'da GPS joylashuv, kamera, sana bo'ladi (telefon rasmida — foydalanuvchi uyi koordinatasi!). Yuklangan rasmni saqlashda EXIF tozalanishi kerak (maxfiylik — 14). .rotate() (EXIF orientation'ga qarab to'g'rilash — aks holda rasm yon tomonda chiqadi), .toBuffer() default EXIF tozalaydi. metadata() (o'lcham/format o'qish — validatsiya). EXIF tozalash — maxfiylik majburiyati.

2.8. Validatsiya va xavfsizlik (sharp bilan)

typescript
// sharp — rasm haqiqiyligini ham tekshiradi (magic bytes — 8.8: 2.4)
async tekshirVaIshla(buffer: Buffer) {
  const metadata = await sharp(buffer).metadata();    // rasm emas  xato (haqiqiy tekshiruv)
  if (!["jpeg", "png", "webp"].includes(metadata.format)) {
    throw new BadRequestException("Faqat rasm");
  }
  if (metadata.width > 8000 || metadata.height > 8000) {   // juda katta (DoS)
    throw new BadRequestException("Rasm o'lchami juda katta");
  }
  // Qayta ishlash  optimallashgan, xavfsiz
  return sharp(buffer).resize(2000, null, { withoutEnlargement: true }).webp({ quality: 82 }).toBuffer();
}

Validatsiya + xavfsizlik: sharp(buffer).metadata() — rasm haqiqiyligini tekshiradi (rasm bo'lmasa xato — magic bytes — 8.8: 2.4 — MIME spoof himoyasi). O'lcham cheklash (juda katta DoS — "decompression bomb"). Qayta ishlangan rasm — xavfsizroq (sharp uni qaytadan kodlaydi — yashirin zararli kod yo'qoladi). sharp — validatsiya + himoya + optimizatsiya birga. ParseFilePipe 8.8-bob bilan birga.

2.9. Fonda qayta ishlash (navbat — 8.22)

typescript
// Og'ir qayta ishlash (ko'p o'lcham, video)  fonda (8.22)
@Post("upload")
@UseInterceptors(FileInterceptor("rasm"))
async yukla(@UploadedFile() file: Express.Multer.File) {
  // 1. Original tez saqlash (yoki vaqtinchalik)
  const url = await this.s3.yukla(file.buffer, `temp/${randomUUID()}`);
  // 2. Qayta ishlash navbatga (so'rovni bloklamaslik — 8.22)
  await this.imageQueue.add("process", { url, userId: file.userId });
  return { url, holat: "qayta ishlanmoqda" };          // darrov javob
}
// Worker: bir necha o'lcham, watermark, optimize (fonda — 8.22)

Fonda qayta ishlash: ko'p o'lcham + watermark + optimize — biroz vaqt oladi so'rovni bloklamaslik uchun navbatga 8.22-bob. Original tez saqlash qayta ishlash worker'da (fonda) tayyor bo'lganda DB yangilash/bildirishnoma 8.23-bob. Oddiy bitta resize — sinxron (tez); ko'p o'lcham/video — fonda. Katta yuklash (galereya) — albatta fonda. UX: foydalanuvchi kutmaydi.

2.10. sharp o'rnatish va libvips

bash
# sharp o'rnatish — libvips (C kutubxona) bilan birga keladi (prebuilt binary)
npm install sharp

#  platformaga mos binary avtomatik yuklanadi. Docker/CI'da boshqa platforma bo'lsa:
npm install --os=linux --cpu=x64 sharp    # (masalan Alpine Linux uchun --libc=musl)
typescript
// Versiya va libvips holatini tekshirish (diagnostika)
import * as sharp from "sharp";
console.log(sharp.versions);        // { vips: "8.15.1", sharp: "0.33.x" }
console.log(sharp.format);          // qo'llab-quvvatlanadigan format ro'yxati (jpeg, webp, avif...)

O'rnatish (libvips): npm install sharp — sharp libvips (yuqori tezlikdagi C rasm kutubxonasi) ustida ishlaydi va platformaga mos prebuilt binary avtomatik yuklanadi (kompilyatsiya shart emas). Docker/CI'da boshqa OS/CPU bo'lsa (masalan lokal Windows, deploy Linux Alpine) — --os/--cpu/--libc bayroqlari bilan to'g'ri binary'ni o'rnating (aks holda "Could not load the sharp module" xatosi — 6-bo'lim, Xato 2). sharp.versions va sharp.format — diagnostika uchun. libvips streaming ishlaydi — katta rasmni ham kam xotira bilan qayta ishlaydi (ImageMagick'dan tez va yengil).

2.11. Geometrik amallar (flip, flop, rotate, extract, trim)

typescript
sharp(input).rotate(90);                                 // 90° soat yo'nalishida burish
sharp(input).rotate();                                   // EXIF orientation'ga qarab avto-to'g'rilash (2.7)
sharp(input).rotate(45, { background: "#ffffff" });      // ixtiyoriy burchak (bo'sh joy fon bilan)
sharp(input).flip();                                     // vertikal aylantirish (yuqoripast)
sharp(input).flop();                                     // gorizontal aylantirish (chapo'ng — ko'zgu)

// extract — aniq to'rtburchakni kesib olish (crop — piksel koordinatasi bilan)
sharp(input).extract({ left: 100, top: 50, width: 400, height: 300 });

// trim — bir xil rangdagi chekka (bo'sh ramka) ni avtomatik olib tashlash
sharp(input).trim();                                     // logotip atrofidagi ortiqcha bo'shliq

Geometrik amallar: rotate(gradus) (burish — burchak bersangiz o'sha darajaga; bo'sh — EXIF avto-to'g'rilash), flip (vertikal — ustma-ust), flop (gorizontal — ko'zgu aksi). extract — aniq koordinata (left/top/width/height) bilan to'rtburchak kesish (resize cover'dan farqli — bu qat'iy piksel; foydalanuvchi tanlagan crop maydonini qo'llashda ishlatiladi). trim — bir xil rangdagi ortiqcha chekkalarni (masalan logotip atrofidagi shaffof/oq bo'shliq) avtomatik qirqadi. extract + resize birga: avval kerakli qismni kesib, keyin o'lchamlash mumkin.

2.12. Format sozlamalari (progressive, lossless, chroma)

typescript
// toFormat — formatni dinamik tanlash (o'zgaruvchi bilan)
const fmt = "webp";
sharp(input).toFormat(fmt as keyof sharp.FormatEnum, { quality: 80 });

// JPEG — progressive (bosqichma-bosqich yuklanadi — sekin internetda UX yaxshi)
sharp(input).jpeg({ quality: 82, progressive: true, mozjpeg: true });   // mozjpeg — yaxshiroq siqish

// PNG — lossless (yo'qotishsiz), siqish darajasi
sharp(input).png({ compressionLevel: 9, palette: true });               // palette — kam rangli PNG kichrayadi

// WebP — lossless yoki lossy
sharp(input).webp({ quality: 80, lossless: false, effort: 4 });         // effort 0-6 (sekinroq = kichikroq)

// AVIF — eng zamonaviy, eng kichik
sharp(input).avif({ quality: 50, effort: 4 });

// GIF — asosan animatsiya uchun (statik rasmga WebP/PNG afzal)
sharp(input, { animated: true }).gif({ colours: 128, effort: 7 });   // rang palitrasi (kam rang = kichik)

Format sozlamalari: toFormat(nom, opsiya) — formatni dinamik (o'zgaruvchi orqali) tanlash (.jpeg()/.webp() — statik muqobil). JPEG uchun progressive: true (rasm bosqichma-bosqich, xiraroqdan aniqroqqa yuklanadi — sekin tarmoqda idrok tezligi yaxshi) va mozjpeg: true (yaxshiroq siqish algoritmi). PNG palette: true (kam rangli rasmni sezilarli kichraytiradi), compressionLevel 0-9. WebP/AVIF lossless (yo'qotishsiz — grafik/logotip uchun) va effort (yuqori = sekinroq lekin kichikroq fayl). GIF (.gif({ colours })) — asosan animatsiya uchun; statik rasmda WebP/PNG samaraliroq. Fotosurat lossy WebP/AVIF; logotip/grafik lossless yoki palette PNG; animatsiya animatsiyali WebP 2.17-bob.

2.13. Rang va filtr amallari (blur, sharpen, grayscale, tint, gamma, negate)

typescript
sharp(input).blur(5);                                    // xiralashtirish (radius — placeholder, maxfiylik)
sharp(input).sharpen();                                  // o'tkirlash (resize'dan keyin aniqlikni tiklaydi)
sharp(input).grayscale();                                // oq-qora (kul rang)
sharp(input).tint({ r: 255, g: 240, b: 220 });           // rang tusi berish (issiq/sovuq)
sharp(input).gamma(2.2);                                 // gamma korreksiya (yorug'lik egri chizig'i)
sharp(input).negate();                                   // rang inversiyasi (negativ)
sharp(input).modulate({ brightness: 1.1, saturation: 1.2, hue: 15 });   // yorqinlik/to'yinganlik/tus
sharp(input).median(3);                                  // shovqin kamaytirish (median filtr)

Rang va filtr: blur(radius) (xiralashtirish — LQIP placeholder Misol 9, yoki maxfiy joyni yopish), sharpen() (o'tkirlash — kichraytirishdan keyin aniqlikni qaytaradi, thumbnail uchun foydali), grayscale() (oq-qora), tint() (rang tusi — brend rangi), gamma() (yorug'lik korreksiyasi), negate() (negativ), modulate() (yorqinlik/to'yinganlik/tus — bir amalda). Amaliy: resize sharpen ketma-ketligi thumbnail sifatini oshiradi; blur + kichik o'lcham placeholder.

2.14. metadata va stats (o'lcham, EXIF, tahlil)

typescript
const meta = await sharp(input).metadata();
// { width, height, format, space, channels, hasAlpha, orientation, exif, density, isProgressive }

const stats = await sharp(input).stats();
// { channels: [{ min, max, mean, ... }], isOpaque, entropy, dominant: { r, g, b } }
// dominant — asosiy rang (galereya foni, placeholder rangi uchun)

metadata va stats: metadata() — rasmni to'liq dekodlamasdan (tez) uning xususiyatlarini o'qiydi: width/height (validatsiya — 2.8), format, hasAlpha (shaffoflik bormi — JPEG'ga o'tkazishdan oldin fon kerakmi bilish), orientation (EXIF burilish — 2.7), exif/density. stats() — piksellarni tahlil qiladi: har kanal mean/min/max, entropy (rasm murakkabligi), dominant (asosiy rang — bo'sh joy foni yoki placeholder rangi sifatida ishlatiladi). metadata() — arzon (header o'qiydi); stats() — qimmatroq (barcha piksel).

2.15. Chiqish: toBuffer vs toFile va stream pipeline

typescript
// toBuffer — natijani xotirada Buffer (S3'ga yuborish, DB, HTTP javob — 5.11)
const buffer = await sharp(input).resize(800).webp().toBuffer();

// toFile — to'g'ridan diskka (format kengaytmadan aniqlanadi)
const info = await sharp(input).resize(800).toFile("output.webp");   // { width, height, size, format }

// Stream pipeline: Multer stream  sharp transform  S3 (buffersiz, kam xotira)
import { PassThrough } from "stream";
const transformer = sharp().resize(1200).webp({ quality: 80 });      // Transform stream
const passthrough = new PassThrough();
transformer.pipe(passthrough);                                       // sharp  S3 stream
fileStream.pipe(transformer);                                        // Multer stream  sharp
await this.s3.upload({ Body: passthrough, Key: key }).promise();     // oqim orqali yuklash

Chiqish rejimlari: toBuffer() — natija xotirada Buffer (S3'ga yuklash, HTTP javobi, base64 — eng ko'p ishlatiladi backend'da). toFile(yo'l) — to'g'ridan diskka yozadi va { width, height, size, format } qaytaradi (lokal saqlash, CLI). Stream pipeline — sharp obyekti (sharp() argumentsiz) Transform stream bo'lib ishlaydi: multer stream sharp S3 zanjiri butun faylni xotiraga yuklamasdan oqim orqali qayta ishlaydi (juda katta fayl — kam xotira). Kichik-o'rta rasm toBuffer (soddaroq); juda katta yoki oqim manba pipeline.

2.16. Xavfsizlik va performance (limitInputPixels, concurrency, cache)

typescript
// Image bomb himoyasi — kichik faylda ulkan piksel (dekompressiya bombasi — DoS)
sharp(input, { limitInputPixels: 268402689 });           // ~16384×16384 default; kamaytirish mumkin
sharp(input, { limitInputPixels: 100_000_000 });         // maks 100 megapiksel
sharp(input, { limitInputPixels: false });               //  o'chirmang (xavfli!)

// failOn — buzuq rasmda qattiq to'xtash (truncated fayl qabul qilmaslik)
sharp(input, { failOn: "error" });

// Global performance sozlamalar (server ishga tushishida bir marta)
sharp.concurrency(4);                                    // parallel thread soni (CPU yadrolari)
sharp.cache({ memory: 200, files: 20, items: 100 });     // libvips operatsiya keshi
sharp.cache(false);                                      // serverless (Lambda)'da keshni o'chirish

Xavfsizlik va performance: limitInputPixelsimage bomb (dekompressiya bombasi) himoyasi: kichik siqilgan fayl ochilganda ulkan piksel maydoniga aylanib xotirani to'ldirishi mumkin (DoS). sharp default ~16384² chegara qo'yadi — kamaytirish mumkin, lekin false qilmang. failOn: "error" — buzuq/kesilgan faylni rad etadi. concurrency — parallel qayta ishlash thread soni (default CPU yadrosi soni); cache — libvips ichki keshi (takroriy amal tez). Serverless (AWS Lambda — 8.24 dan tashqari) muhitida sharp.cache(false) va concurrency(1) ko'pincha tavsiya etiladi (izolyatsiya, xotira chegarasi).

2.17. Animatsiyali rasm va cloud storage

typescript
// Animatsiyali GIF/WebP — barcha kadrlarni saqlab qayta ishlash
const anim = await sharp(input, { animated: true })      //  animated: true — barcha kadr
  .resize(300)                                           // barcha kadrga qo'llaniladi
  .webp()                                                // animatsiyali WebP (GIF'dan kichik)
  .toBuffer();

// Cloudinary — tashqi rasm xizmati (URL orqali transform, sharp o'rniga/bilan)
// Yuklash: cloudinary.uploader.upload(buffer)  optimallash URL'da (w_300,f_auto,q_auto)
// sharp — o'z serveringizda nazorat; Cloudinary/imgproxy — CDN + on-the-fly transform

Animatsiyali va cloud: { animated: true } — GIF/animatsiyali WebP ni qayta ishlashda barcha kadrlarni saqlaydi (aks holda faqat birinchi kadr qoladi); resize barcha kadrga qo'llaniladi. Animatsiyali GIF WebP konvertatsiya hajmni keskin kamaytiradi. Cloud storage/xizmat — uchta yondashuv: (1) sharp + S3 + CDN — rasmni o'z serveringizda sharp bilan qayta ishlab, natijani S3'ga (yoki har qanday obyekt-ombor) yuklaysiz, oldiga CloudFront/Cloudflare CDN qo'yasiz. To'liq nazorat, arzon, lekin kod va infratuzilma o'zingizda. (2) Cloudinary — rasmni yuklaysiz, barcha transform URL parametri orqali on-the-fly bo'ladi (https://res.cloudinary.com/.../w_300,f_auto,q_auto/rasm.jpgf_auto brauzerga mos format tanlaydi, q_auto avto sifat), built-in CDN va kesh bilan. Tez ishga tushadi, lekin trafik oshgani sari qimmat. (3) imgproxy (yoki Thumbor) — o'zingiz hostlaydigan on-the-fly transform serveri: original S3'da turadi, imgproxy imzolangan URL orqali kerakli o'lchamni real-time yasab beradi (Cloudinary'ning ochiq muqobili). Kichik-o'rta loyiha sharp + S3 + CDN (arzon, nazorat, oldindan variant); tez prototip yoki media-og'ir loyiha Cloudinary/imgproxy (on-the-fly, CDN tayyor). Ko'pincha aralash: yuklashda sharp bilan bir necha asosiy variant + CDN kesh.

2.18. Best practices (rasm)

text
   Yuklangan rasmni QAYTA ISHLA (xom saqlama — 2.1)
   WebP/AVIF format + siqish (quality ~80 — 2.4)
   Bir necha o'lcham (responsive — thumbnail/medium/large — 2.5)
   EXIF tozalash (maxfiylik — GPS — 14, 2.7) + .rotate()
   sharp validatsiya (rasm haqiqiyligi, o'lcham — DoS — 2.8)
   Og'ir  fonda (navbat — 2.9, 8.22)
   Original saqlash (qayta ishlash kerak bo'lsa) yoki o'chirish
   S3/CDN 5.11-bob + lazy loading (frontend — 13)
   withoutEnlargement (kichikni kattalashtirmaslik — 2.3)
   limitInputPixels (image bomb himoyasi — DoS — 2.16)
   resize  sharpen (thumbnail aniqligi — 2.13)
   animated: true (GIF/animatsiyali WebP — 2.17)
   Serverless'da sharp.cache(false) (2.16)

3. Sintaksis — tez ma'lumotnoma

typescript
// sharp zanjir (2.2)
sharp(input).resize(en, balandlik, { fit }).webp({ quality }).toBuffer()

// Resize 2.3-bob: fit: "cover"|"contain"|"inside"; withoutEnlargement
// Format 2.4-bob: .webp/.jpeg/.avif/.png({ quality })
// Composite 2.6-bob: .composite([{ input: logo, gravity, blend }])
// Metadata 2.7-bob: .metadata()  { width, height, format }
// EXIF 2.7-bob: .rotate().withMetadata({ exif: {} })
// Geometrik 2.11-bob: .rotate(gradus) .flip() .flop() .extract({left,top,width,height}) .trim()
// Format 2.12-bob: .toFormat(nom) .jpeg({progressive,mozjpeg}) .png({palette}) .webp({lossless,effort})
// Filtr 2.13-bob: .blur(r) .sharpen() .grayscale() .tint() .gamma() .negate() .modulate()
// Stats 2.14-bob: .stats()  { dominant, entropy, channels }
// Chiqish 2.15-bob: .toBuffer() / .toFile(yo'l) / sharp() Transform stream (pipe)
// Xavfsizlik 2.16-bob: sharp(in, { limitInputPixels, failOn }); sharp.concurrency()/cache()
// Animatsiya 2.17-bob: sharp(in, { animated: true })

4. Batafsil kod namunalari

Misol 1 — Image service (asosiy — 2.2, 2.4)

typescript
@Injectable()
export class ImageService {
  constructor(private s3: S3Service) {}

  async optimallash(buffer: Buffer): Promise<Buffer> {
    return sharp(buffer)
      .rotate()                                       // EXIF orientation (2.7)
      .resize(2000, null, { withoutEnlargement: true, fit: "inside" })   // maks 2000px (2.3)
      .webp({ quality: 82 })                          // WebP siqish (2.4)
      .toBuffer();                                    // EXIF avtomatik tozalanadi (2.7)
  }
}

Misol 2 — Bir necha o'lcham (responsive — 2.5)

typescript
@Injectable()
export class ImageVariantService {
  private readonly OLCHAMLAR = [
    { nom: "thumb", en: 150 },
    { nom: "small", en: 400 },
    { nom: "medium", en: 800 },
    { nom: "large", en: 1600 },
  ];

  async variantlarYarat(buffer: Buffer, prefix: string): Promise<Record<string, string>> {
    const natijalar: Record<string, string> = {};

    // Parallel (Promise.all — tez)
    await Promise.all(this.OLCHAMLAR.map(async (o) => {
      const out = await sharp(buffer)
        .rotate()
        .resize(o.en, null, { withoutEnlargement: true, fit: "inside" })
        .webp({ quality: o.en <= 400 ? 75 : 82 })     // kichikga ko'proq siqish
        .toBuffer();
      natijalar[o.nom] = await this.s3.yukla(out, `${prefix}-${o.nom}.webp`);
    }));

    return natijalar;                                 // { thumb, small, medium, large } URL
  }
}

Misol 3 — Avatar (crop — 2.3)

typescript
async avatar(buffer: Buffer, userId: string): Promise<string> {
  const out = await sharp(buffer)
    .rotate()
    .resize(256, 256, { fit: "cover", position: "centre" })   // kvadrat (cover — kesadi — 2.3)
    .webp({ quality: 85 })
    .toBuffer();
  return this.s3.yukla(out, `avatars/${userId}.webp`);
}
// Dumaloq avatar — frontend CSS (border-radius); yoki SVG mask (sharp)

Misol 4 — Watermark (2.6)

typescript
async watermark(buffer: Buffer): Promise<Buffer> {
  const meta = await sharp(buffer).metadata();
  // Logo o'lchamini rasmga moslab (10%)
  const logoEn = Math.round((meta.width || 800) * 0.15);
  const logo = await sharp("assets/logo.png").resize(logoEn).png().toBuffer();

  return sharp(buffer)
    .composite([{ input: logo, gravity: "southeast", blend: "over" }])   // o'ng past (2.6)
    .webp({ quality: 82 })
    .toBuffer();
}

Misol 5 — Validatsiya (sharp + DoS himoyasi — 2.8)

typescript
@Injectable()
export class ImageValidationPipe implements PipeTransform {
  async transform(file: Express.Multer.File) {
    if (!file) throw new BadRequestException("Rasm yo'q");
    try {
      const meta = await sharp(file.buffer).metadata();   // rasm emas  xato (8.8: 2.4)
      if (!["jpeg", "png", "webp", "gif"].includes(meta.format!)) {
        throw new BadRequestException("Faqat rasm fayllari");
      }
      if ((meta.width || 0) > 10000 || (meta.height || 0) > 10000) {
        throw new BadRequestException("Rasm o'lchami juda katta (DoS)");   // (2.8)
      }
      return file;
    } catch {
      throw new BadRequestException("Yaroqsiz rasm");
    }
  }
}

// Controller
@Post("upload")
@UseInterceptors(FileInterceptor("rasm"))
yukla(@UploadedFile(ImageValidationPipe) file: Express.Multer.File) {
  return this.imageService.qaytaIshla(file.buffer);
}

Misol 6 — To'liq upload + variant (5.11 davomi)

typescript
@Post("products/:id/images")
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles(Rol.ADMIN)                                     // (8.7)
@UseInterceptors(FilesInterceptor("rasmlar", 10))    // ko'p rasm (8.8)
async mahsulotRasmlari(
  @Param("id") productId: string,
  @UploadedFiles() files: Express.Multer.File[],
) {
  const natijalar = [];
  for (const file of files) {
    await sharp(file.buffer).metadata();              // validatsiya (2.8)
    const variantlar = await this.variantService.variantlarYarat(   // 4 o'lcham (Misol 2)
      file.buffer, `products/${productId}/${randomUUID()}`,
    );
    natijalar.push(variantlar);
  }
  await this.productsService.rasmlarQosh(productId, natijalar);   // DB'ga URL (5.11: 2.12)
  return natijalar;
}

Misol 7 — Fonda qayta ishlash (navbat — 2.9)

typescript
@Post("upload")
@UseInterceptors(FileInterceptor("rasm"))
async yukla(@UploadedFile(ImageValidationPipe) file: Express.Multer.File, @CurrentUser() user) {
  // 1. Original vaqtinchalik saqlash (tez)
  const tempKey = `temp/${randomUUID()}`;
  await this.s3.yukla(file.buffer, tempKey);
  // 2. Qayta ishlash navbatga (8.22)
  const job = await this.imageQueue.add("process", { tempKey, userId: user.id });
  return { jobId: job.id, holat: "qayta ishlanmoqda" };
}

@Processor("image")
export class ImageProcessor extends WorkerHost {
  async process(job: Job) {
    const buffer = await this.s3.yuklab(job.data.tempKey);   // original
    const variantlar = await this.variantService.variantlarYarat(buffer, `final/${job.id}`);
    await this.s3.ochir(job.data.tempKey);                    // temp tozalash
    await this.notify.yubor(job.data.userId, "Rasm tayyor", variantlar);   // (8.23)
  }
}

Misol 8 — Format aniqlash (brauzer qo'llab-quvvatlasa AVIF — 2.4)

typescript
// Accept header'ga qarab format (AVIF > WebP > JPEG)
@Get("images/:id")
async rasm(@Param("id") id: string, @Headers("accept") accept: string, @Res() res: Response) {
  const buffer = await this.s3.yuklab(`images/${id}`);
  let out: Buffer, contentType: string;

  if (accept?.includes("image/avif")) {
    out = await sharp(buffer).avif({ quality: 70 }).toBuffer();   // eng kichik (2.4)
    contentType = "image/avif";
  } else if (accept?.includes("image/webp")) {
    out = await sharp(buffer).webp({ quality: 80 }).toBuffer();
    contentType = "image/webp";
  } else {
    out = await sharp(buffer).jpeg({ quality: 82 }).toBuffer();   // eski brauzer
    contentType = "image/jpeg";
  }
  res.setHeader("Content-Type", contentType);
  res.setHeader("Cache-Control", "public, max-age=31536000");      // CDN kesh (1 yil)
  res.send(out);
}
//  Production'da CDN (Cloudflare/imgproxy) afzal — real-time emas

Misol 9 — Placeholder/blur (LQIP — UX)

typescript
// Kichik blur placeholder (rasm yuklanguncha ko'rsatish — UX, 13)
async blurPlaceholder(buffer: Buffer): Promise<string> {
  const tiny = await sharp(buffer)
    .resize(20)                                       // juda kichik (20px)
    .blur()
    .webp({ quality: 30 })
    .toBuffer();
  return `data:image/webp;base64,${tiny.toString("base64")}`;   // inline base64 (DB'ga)
}
// Frontend: blur placeholder  asosiy rasm yuklangach almashtirish (silliq UX)

Misol 10 — Foydalanuvchi tanlagan crop (extract — 2.11)

typescript
// Frontend crop editor koordinatalarini qo'llash (avatar tahrirlash)
interface CropDto {
  left: number; top: number; width: number; height: number;
}

async cropVaSaqla(buffer: Buffer, crop: CropDto, userId: string): Promise<string> {
  const meta = await sharp(buffer).metadata();
  // Chegara tekshiruvi (crop rasm ichida bo'lishi shart — aks holda sharp xato beradi)
  if (crop.left + crop.width > (meta.width || 0) || crop.top + crop.height > (meta.height || 0)) {
    throw new BadRequestException("Crop maydoni rasm chegarasidan tashqarida");
  }
  const out = await sharp(buffer)
    .rotate()                                            // EXIF 2.7-bob — koordinatadan oldin to'g'rilash
    .extract(crop)                                       // aniq to'rtburchak kesish (2.11)
    .resize(512, 512, { fit: "cover" })                 // standart o'lchamga (2.3)
    .sharpen()                                           // kesish/o'lchamdan keyin o'tkirlash (2.13)
    .webp({ quality: 85 })
    .toBuffer();
  return this.s3.yukla(out, `avatars/${userId}.webp`);
}

Misol 11 — Stream pipeline (Multer sharp S3, kam xotira — 2.15)

typescript
// Katta rasmni butun xotiraga yuklamasdan oqim orqali qayta ishlash
import { PassThrough } from "stream";
import { Upload } from "@aws-sdk/lib-storage";

@Injectable()
export class ImageStreamService {
  constructor(private s3: S3ClientProvider) {}

  async streamYukla(fileStream: NodeJS.ReadableStream, key: string): Promise<string> {
    // sharp() argumentsiz — Transform stream sifatida ishlaydi (2.15)
    const transformer = sharp({ limitInputPixels: 100_000_000 })   // image bomb himoyasi (2.16)
      .rotate()
      .resize(1600, null, { withoutEnlargement: true, fit: "inside" })
      .webp({ quality: 82 });

    const passthrough = new PassThrough();
    fileStream.pipe(transformer).pipe(passthrough);      // Multer  sharp  S3 oqimi

    const upload = new Upload({
      client: this.s3.client,
      params: { Bucket: "media", Key: `${key}.webp`, Body: passthrough, ContentType: "image/webp" },
    });
    await upload.done();
    return `https://cdn.example.com/${key}.webp`;
  }
}
//  Diskless streaming — RAM'da butun fayl yo'q; juda katta yuklash uchun

Misol 12 — Animatsiyali GIF WebP (2.17)

typescript
// Animatsiyali GIF ni kichikroq animatsiyali WebP ga aylantirish
async gifOptimallash(buffer: Buffer): Promise<Buffer> {
  const meta = await sharp(buffer, { animated: true }).metadata();
  // pages — kadr soni; pageHeight — bitta kadr balandligi
  if ((meta.pages || 1) > 1) {
    return sharp(buffer, { animated: true })             // barcha kadr (2.17)
      .resize(400, null, { withoutEnlargement: true })
      .webp({ quality: 75, effort: 4 })                  // animatsiyali WebP (GIF'dan ancha kichik)
      .toBuffer();
  }
  // Statik GIF  oddiy WebP
  return sharp(buffer).resize(400, null, { withoutEnlargement: true }).webp({ quality: 80 }).toBuffer();
}

Misol 13 — Image modul (to'liq)

text
  src/images/
  ├── image.service.ts           (optimallash — Misol 1)
  ├── image-variant.service.ts   (responsive — Misol 2)
  ├── image-stream.service.ts    (stream pipeline — Misol 11)
  ├── image-validation.pipe.ts   (validatsiya + limitInputPixels — Misol 5, 2.16)
  ├── image.processor.ts         (fonda — Misol 7)
  ├── images.controller.ts       (upload + crop + serve — Misol 6, 8, 10)
  └── images.module.ts

  Ilova ishga tushishida (main.ts):
  sharp.concurrency(4); sharp.cache({ memory: 200 });   // performance 2.16-bob

  Oqim:
  Upload (5.11/8.8)  validatsiya (sharp — 2.8)  variant yaratish (Misol 2)
   S3 5.11-bob  DB'ga URL'lar (5.11: 2.12)  frontend srcset (13)
  Og'ir  navbat (Misol 7); serve  CDN kesh (Misol 8)

5. To'g'ri va noto'g'ri holatlar

1) Xom rasmni saqlash

text
 8MB original (sekin, joy — 2.1)
 resize + WebP (200KB)

2) Bir o'lcham hamma joyga

text
 ro'yxatda 2000px rasm (behuda — 2.5)
 responsive (thumbnail/medium/large)

3) EXIF tozalanmagan

text
 GPS joylashuv oshkor (maxfiylik — 14, 2.7)
 EXIF tozalash + .rotate()

4) Validatsiyasiz (faqat MIME)

text
 MIME spoof (8.8: 2.4)
 sharp metadata (haqiqiy tekshiruv — 2.8)

5) Og'ir qayta ishlash sinxron

text
 10 rasm × 4 o'lcham so'rovda (timeout — 2.9)
 navbat (fonda)

6. Keng tarqalgan xatolar va yechimlari

Xato 1 — Rasm yon tomonda (aylangan)

Sababi: EXIF orientation 2.7-bob. Yechimi: .rotate().

Xato 2 — sharp o'rnatilmaydi ("Could not load the sharp module" — platforma)

Sababi: native binary (libvips) platformaga mos emas (lokal Windows, deploy Linux/Alpine — 2.10). Yechimi: to'g'ri platforma binary'sini o'rnatish (npm install --os=linux --cpu=x64 --libc=musl sharp); Docker (10).

Xato 2b — Animatsiyali GIF birinchi kadr bo'lib qoladi

Sababi: animated: true berilmagan 2.17-bob. Yechimi: sharp(input, { animated: true }).

Xato 3 — Xotira ko'p (katta rasm)

Sababi: juda katta rasm 2.8-bob. Yechimi: o'lcham cheklash; sharp({ limitInputPixels }).

Xato 4 — Sekin (har serve'da qayta ishlash)

Sababi: real-time qayta ishlash 2.4-bob. Yechimi: oldindan variant; CDN kesh (Misol 8).

Xato 5 — Format eski brauzerda ochilmaydi

Sababi: AVIF eski brauzer 2.4-bob. Yechimi: Accept header (Misol 8) / WebP fallback.

Xato 6 — Og'ir yuklash timeout

Sababi: sinxron 2.9-bob. Yechimi: navbat.


7. Integratsiya — bu mavzu stack'ning qayerida uchraydi

  • Fayl yuklash (5.11, 8.8): rasm qabul.
  • S3 5.11-bob: saqlash.
  • Navbat 8.22-bob: fonda qayta ishlash.
  • Validatsiya 8.8-bob: magic bytes + sharp.
  • Push 8.23-bob: "rasm tayyor".
  • Xavfsizlik (14): EXIF, DoS.
  • Next.js Image (13): responsive, optimize.
  • SEO (13): sayt tezligi.

8. Eng yaxshi amaliyotlar (best practices)

  • Yuklangan rasmni qayta ishla (xom saqlama — 2.1).
  • WebP/AVIF + siqish (quality ~80 — 2.4).
  • Bir necha o'lcham (responsive — 2.5).
  • EXIF tozalash + .rotate() (maxfiylik — 14, 2.7).
  • sharp validatsiya (rasm haqiqiyligi, DoS — 2.8).
  • Og'ir fonda (navbat — 2.9).
  • S3/CDN + kesh (5.11, Misol 8).
  • withoutEnlargement (kichikni kattalashtirmaslik — 2.3).
  • Blur placeholder (UX — Misol 9).
  • Original saqlash/o'chirish (qaror — 2.10).

9. Amaliy loyiha: "Rasm Qayta Ishlash Tizimi"

Rasm qayta ishlashni amalda mustahkamlash.

Maqsad

To'liq rasm tizimi: upload, validatsiya, responsive variant, watermark, EXIF, fonda.

Talablar (requirements)

  1. Optimallash: resize + WebP + EXIF (Misol 1, 2.4, 2.7).
  2. Responsive: bir necha o'lcham (Misol 2, 2.5).
  3. Avatar: crop kvadrat (Misol 3, 2.3).
  4. Watermark: logo (Misol 4, 2.6).
  5. Validatsiya: sharp + DoS (Misol 5, 2.8).
  6. Upload: ko'p rasm + variant (Misol 6, 8.8).
  7. Fonda: navbat (Misol 7, 2.9).
  8. Format: Accept header (AVIF/WebP — Misol 8, 2.4).
  9. Placeholder: blur LQIP (Misol 9).
  10. CDN kesh: serve optimizatsiya (Misol 8).

Maslahatlar (hint)

  • .rotate() (EXIF — 2.7, 1-xato).
  • Responsive (2.5, 2-holat).
  • EXIF tozalash (14, 3-holat).
  • sharp validatsiya (2.8, 4-holat).
  • Og'ir navbat (2.9, 6-xato).
  • CDN kesh (Misol 8, 4-xato).

"Tayyor" mezonlari (acceptance criteria)

  • Optimallash (WebP).
  • Responsive variant.
  • Avatar (crop).
  • Watermark.
  • Validatsiya (sharp).
  • Upload (ko'p).
  • Fonda (navbat).
  • Format (Accept).
  • Placeholder.
  • CDN kesh.

Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.


10. Xulosa va keyingi bobga ko'prik

Bu bobda rasm qayta ishlashni o'rgandik:

  • Nega kerak (tezlik, joy, maxfiylik — 2.1); sharp (zanjir — 2.2); resize (fit strategiyalari — 2.3).
  • Format/siqish (WebP/AVIF — 2.4); responsive (bir necha o'lcham — 2.5); watermark 2.6-bob.
  • EXIF tozalash (maxfiylik — 2.7); validatsiya (sharp — DoS — 2.8); fonda (navbat — 2.9).

Keyingi bob — 8.25: Multi-tenancy (SaaS). Rasmni bildik; endi SaaS arxitekturasining asosini — multi-tenancy (bir ilova, ko'p mijoz-kompaniya, ma'lumot izolyatsiyasi) — o'rganamiz. Har B2B SaaS (CRM, hisobot tizimi) uchun zarur.


Foydalanilgan rasmiy/ishonchli manbalar

  • sharp rasmiy hujjati — API to'liq: resize (fit/position/background), composite, metadata/stats, rotate/extract/trim, format opsiyalari (jpeg/png/webp/avif/gif), limitInputPixels, global concurrency/cache.
  • libvips hujjati — sharp asosidagi C kutubxona: streaming arxitektura, xotira/tezlik xarakteristikasi.
  • web.dev — zamonaviy rasm formatlari — WebP/AVIF taqqoslash, responsive rasm (srcset/sizes), <picture> va format fallback.
  • MDN — responsive images va <picture> elementi — brauzer format tanlash, lazy loading.
  • NestJS rasmiy hujjati — fayl yuklash (FileInterceptor/FilesInterceptor), ParseFilePipe, custom PipeTransform.
  • AWS SDK v3 (@aws-sdk/lib-storage Upload) — stream orqali S3'ga yuklash (pipeline — Misol 11).
  • OWASP — fayl yuklash xavfsizligi — MIME/kontent validatsiya, decompression bomb (image bomb) himoyasi.

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
8.24-bob: Rasm/media qayta ishlash — sharp — Wisar