Sharp — rasmlarni qayta ishlash, resize, compress
Muammodan boshlaylik
Foydalanuvchi profil rasmi yukladi — 4000×3000 piksel, 8 MB. Siz uni shunday saqlaysiz. Sahifada 50 ta profil rasmi ko'rsatilganda brauzer 400 MB yuklab oladi. Sahifa 30 soniya yuklanadi.
Yoki boshqa holat: e-commerce sayt. Har bir mahsulot rasmi original holda — 5-10 MB. Sahifani 20 mahsulot bilan ochsangiz — internet chiqimi 100-200 MB. Ko'pchilik foydalanuvchi sahifani yopib ketadi.
Yechim: serverda rasmni qabul qilingan zahoti qayta ishlash. Kichraytirish, siqish, formatlash.
Sharp nima?
Sharp — Node.js da rasmlarni qayta ishlash uchun eng tez kutubxona. C++ da yozilgan libvips ustida qurilgan — ImageMagick dan 4-5 marta tez.
npm install sharp
Asosiy amallar
Resize — o'lchamni o'zgartirish
const sharp = require('sharp');
// Fayldan faylga
await sharp('kirish.jpg')
.resize(300, 300) // ← kenglik × balandlik
.toFile('chiqish.jpg');
// Nisbatni saqlagan holda resize
await sharp('kirish.jpg')
.resize(800, null) // ← kenglik 800, balandlik avtomatik
.toFile('chiqish.jpg');
await sharp('kirish.jpg')
.resize(null, 600) // ← balandlik 600, kenglik avtomatik
.toFile('chiqish.jpg');
// Kvadrat crop — profil rasmlari uchun
await sharp('kirish.jpg')
.resize(300, 300, {
fit: 'cover', // ← to'ldiradi va kerakli qismini kesadi
position: 'center', // ← markazdan kesadi
})
.toFile('profil.jpg');
fit parametri nima uchun muhim? Turli nisbatdagi rasmlarni bir xil o'lchamga keltirish kerak. cover — rasm to'liq to'ldiriladi (qismlari kesilishi mumkin). contain — rasm ichiga sig'diriladi (bo'sh joy qolishi mumkin). fill — cho'ziladi (nisbat buziladi).
Format o'zgartirish va siqish
// JPEG ga o'tkazish va siqish
await sharp('kirish.png')
.jpeg({ quality: 80 }) // ← 80% sifat, hajm ~60% kamayadi
.toFile('chiqish.jpg');
// PNG siqish
await sharp('kirish.png')
.png({ compressionLevel: 9 }) // ← 0-9, 9 = maksimal siqish
.toFile('chiqish.png');
// WebP — zamonaviy format, JPEG dan ~30% kichik
await sharp('kirish.jpg')
.webp({ quality: 80 })
.toFile('chiqish.webp');
Nima uchun WebP? Xuddi shunday sifatda JPEG dan 25-35% kichik. Zamonaviy brauzerlarning barchasi qo'llab-quvvatlaydi. Sayt tezligini sezilarli oshiradi.
Multer bilan birga ishlatish
Oldingi darsda Multer ning memoryStorage ni eslab qoldingizmi? Aynan shu yerda kerak bo'ladi:
const express = require('express');
const multer = require('multer');
const sharp = require('sharp');
const path = require('path');
const fs = require('fs');
const app = express();
// Multer — diskka emas, xotiraga saqlaydi
const upload = multer({
storage: multer.memoryStorage(), // ← buffer sifatida xotirada
limits: { fileSize: 10 * 1024 * 1024 }, // ← 10 MB (original)
fileFilter: (req, file, cb) => {
const allowed = ['image/jpeg', 'image/png', 'image/webp'];
allowed.includes(file.mimetype) ? cb(null, true) : cb(new Error('Faqat rasm'));
}
});
// Profil rasmi yuklash
app.post('/profil/rasm', upload.single('rasm'), async (req, res) => {
if (!req.file) {
return res.status(400).json({ error: 'Rasm yuborilmadi' });
}
const filename = `profil-${Date.now()}.webp`;
const outputPath = path.join('uploads', filename);
await sharp(req.file.buffer) // ← xotiradagi buffer
.resize(300, 300, {
fit: 'cover',
position: 'center',
})
.webp({ quality: 85 }) // ← WebP formatga, 85% sifat
.toFile(outputPath);
res.json({
message: 'Rasm saqlandi',
url: `/uploads/${filename}`,
});
});
app.use('/uploads', express.static('uploads'));
app.listen(3000);
Bir nechta o'lcham yaratish (thumbnails)
E-commerce saytlarda bir rasmning bir nechta varianti kerak: kichik (ro'yxat uchun), o'rta (karta uchun), katta (batafsil sahifa uchun):
app.post('/mahsulot/rasm', upload.single('rasm'), async (req, res) => {
if (!req.file) {
return res.status(400).json({ error: 'Rasm yuborilmadi' });
}
const baseName = `mahsulot-${Date.now()}`;
// Barcha o'lchamlarni parallel yaratish
await Promise.all([
sharp(req.file.buffer)
.resize(100, 100, { fit: 'cover' })
.webp({ quality: 80 })
.toFile(`uploads/${baseName}-kichik.webp`),
sharp(req.file.buffer)
.resize(400, 400, { fit: 'cover' })
.webp({ quality: 85 })
.toFile(`uploads/${baseName}-orta.webp`),
sharp(req.file.buffer)
.resize(1200, null) // ← kenglik 1200, balandlik avtomatik
.webp({ quality: 90 })
.toFile(`uploads/${baseName}-katta.webp`),
]);
res.json({
kichik: `/uploads/${baseName}-kichik.webp`,
orta: `/uploads/${baseName}-orta.webp`,
katta: `/uploads/${baseName}-katta.webp`,
});
});
Promise.all nima uchun? Uchta rasm ketma-ket yaratilsa — birinchisi tugamaguncha ikkinchisi boshlanmaydi. Promise.all bilan barchasi parallel ishlaydi — 3 marta tez.
Metadata o'qish
app.post('/rasm-info', upload.single('rasm'), async (req, res) => {
const metadata = await sharp(req.file.buffer).metadata();
res.json({
format: metadata.format, // ← 'jpeg', 'png', 'webp'
kenglik: metadata.width,
balandlik: metadata.height,
hajm: req.file.size,
kanallar: metadata.channels, // ← 3 (RGB) yoki 4 (RGBA)
});
});
Watermark qo'shish
app.post('/rasm-watermark', upload.single('rasm'), async (req, res) => {
const watermark = await fs.promises.readFile('watermark.png'); // ← shaffof PNG
const filename = `wm-${Date.now()}.webp`;
await sharp(req.file.buffer)
.resize(1200, null)
.composite([{
input: watermark,
gravity: 'southeast', // ← o'ng pastga
blend: 'over',
}])
.webp({ quality: 85 })
.toFile(`uploads/${filename}`);
res.json({ url: `/uploads/${filename}` });
});
Tez-tez uchraydigan xatolar
1. Katta rasmda xotira tugashi
// ❌ — 50 MB li rasm xotiraga yuklanadi
const upload = multer({
storage: multer.memoryStorage(),
// limits yo'q!
});
Sababi: memoryStorage faylni RAMga yuklaydi. 100 ta parallel so'rovda har biri 50 MB bo'lsa — 5 GB RAM kerak. Server crash bo'ladi.
// ✅ — hajmni cheklang
const upload = multer({
storage: multer.memoryStorage(),
limits: { fileSize: 10 * 1024 * 1024 } // ← 10 MB maksimal
});
2. Sharp zanjirida await unutilishi
// ❌ — await yo'q, fayl yaratilishini kutmaymiz
sharp(buffer).resize(300, 300).toFile('chiqish.jpg');
res.json({ message: 'Saqlandi' }); // ← aslida saqlanmagan!
// ✅ — await bilan kutish
await sharp(buffer).resize(300, 300).toFile('chiqish.jpg');
res.json({ message: 'Saqlandi' });
3. Animatsiyali GIF ni Sharp bilan qayta ishlash
// ❌ — Sharp GIF animatsiyasini qo'llab-quvvatlamaydi (default)
await sharp('animatsiya.gif').resize(300, 300).toFile('chiqish.gif');
// Natija: faqat birinchi kadr saqlanadi
Sababi: Sharp libvips bilan ishlaydi — GIF animatsiyasi uchun alohida { animated: true } parametri kerak yoki gifsicle kabi maxsus kutubxona ishlatiladi.
4. Original faylni o'chirish
// ❌ — resize qilganingiz original faylni o'chiradi
await sharp('original.jpg')
.resize(300)
.toFile('original.jpg'); // ← bir xil nom!
Sababi: Sharp fayl yozayotganda asl fayl o'qiladi va ustiga yoziladi — natija buzilishi mumkin. Har doim boshqa fayl nomiga yozing.
Xulosa
- Sharp — Node.js da eng tez rasm qayta ishlash kutubxonasi
resize()— o'lchamni o'zgartiradi,fit: 'cover'nisbatni saqlaydiwebp({ quality: 80-90 })— JPEG dan kichik, sifat yaxshi- Multer
memoryStorage+ Sharp — fayl diskka saqlanmasdan qayta ishlanadi Promise.all— bir nechta o'lcham parallel yaratiladi, tezroq
Keyingi dars: 34-dars: Amazon S3 — fayl saqlash, upload, delete, signed URL