IlmHamroh
JavaScript Full-stack/29-qism. Backend integratsiyalar: fayl, to'lov, bot, realtime7/69-dars16 daqiqa
Mundarija (25)

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, limitInputPixels siz 642 KB lik fayl ochilganda 644 MB xotira yeydi.

Bu darsda

  • cover, contain, fill, inside, outside strategiyalarini 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 limitInputPixels bilan 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.

bash
npm i sharp@0.35.5

O'rnatishda tayyor binar (Windows, Linux, macOS uchun) yuklanadi — kompilyator kerak emas. Kanon mashinasida:

text
libvips 8.18.7 | concurrency 16 | SIMD true
manba: 3840×2160 JPEG, 1009 KB

API — zanjir (chain): kirish → amallar → format → chiqish.

js
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:

text
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×80

O'lchamlar qayerdan kelishini toza JS bilan hisoblaymiz:

js
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:

text
cover    400×400
contain  400×400
fill     400×400
inside   400×225
outside  711×400

Kanon 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. cover markazdan 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):

text
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 ms

Xulosalar:

  • 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:

text
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:

  1. Sukut. sharp chiqishga metama'lumot yozmaydi — GPS tashlandi. Yaxshi. Lekin Orientation ham tashlandi, piksellar esa burilmagan. Telefon rasmni "yonboshlab" saqlab, "90° bur" deb belgilagan edi; belgi yo'qoldi — rasm yonboshlab ko'rinadi.
  2. rotate() (argumentsiz) — piksellarni EXIF bo'yicha haqiqatan buradi, keyin belgi kerak emas. 400×711 — tik rasm, to'g'ri.
  3. 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°):

js
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:

text
{ eni: 450, boyi: 800 }
400 × 711

Kanonning rotate() bilan: 400×711 qatori aynan shu hisob.

Diqqat: rotate() ni zanjirning boshida chaqiring — resize dan 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:

text
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:

js
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:

text
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:

  1. metadata() — sarlavhadan o'lchamni oladi, piksellarni ochmaydi (0.5 ms). Avval shu bilan tekshiring.
  2. sharp(kirish, { limitInputPixels: 40_000_000 }) — har ochishda limit; oshsa Input 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:

text
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 (blurhash paketi, 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

text
16 parallel: 677 ms; ketma-ket 1 ta ~ yuqoridagi webp vaqti

Kanon 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 korinishlar yangilanadi.
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

  1. Piksel bomba. Kichik fayl — ulkan xotira. Himoya: metadata() + limitInputPixels.
  2. GPS sizishi. Asl rasm boshqa foydalanuvchilarga ko'rsatiladi — uy manzili bilan. Himoya: faqat qayta kodlangan ko'rinishlarni ko'rsating; asl faylni faqat egasiga.
  3. Polyglot. Rasm ham, skript ham bo'lgan fayl. Himoya: qayta kodlash — yangi fayl faqat piksellardan.
  4. Buzilgan fayl. Ataylab buzilgan sarlavha kutubxona xatosini chaqiradi. Himoya: xatoni ushlab, RasmXato ga aylantiring; jarayon yiqilmasin.
  5. 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
js
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:

text
[ '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
js
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:

text
{ 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):

ts
// 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.Metadata namespace 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):

text
✔ 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.725

Hammasi ✔, o'tkazilgan (﹣) yo'q. Vaqtlar har ishga tushishda boshqacha.

Commit xabari (modul uchun):

bash
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

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!