Multer — fayl yuklash, storage, filter, size limit
Muammodan boshlaylik
Foydalanuvchi profil rasmi yuklashi kerak. express.json() bor — ishlamaydi. express.urlencoded() bor — u ham emas. Nima muammo?
app.post('/upload', (req, res) => {
console.log(req.body); // ← {} bo'sh!
console.log(req.file); // ← undefined!
});
Sababi: rasm — JSON emas, matn ham emas. Bu binary data — ikkilik ma'lumot. Brauzer uni multipart/form-data formatida yuboradi. express.json() bu formatni tushunmaydi.
Multer aynan shu formatni qayta ishlaydi.
Multer nima?
Multer — Express uchun multipart/form-data middleware. Fayllarni serverga qabul qilib, saqlaydi va req.file / req.files ga ma'lumot qo'yadi.
npm install multer
Eng oddiy misol
const express = require('express');
const multer = require('multer');
const path = require('path');
const app = express();
// Multer ni sozlash — fayllar qaerga saqlansin
const upload = multer({ dest: 'uploads/' }); // ← uploads/ papkasiga saqlaydi
// Bitta fayl yuklash
app.post('/upload', upload.single('rasm'), (req, res) => {
// ↑ form dagi input name="rasm"
console.log(req.file); // ← fayl haqida ma'lumot
res.json({ message: 'Yuklandi', fayl: req.file });
});
req.file qanday ko'rinadi:
{
"fieldname": "rasm",
"originalname": "profil.jpg",
"encoding": "7bit",
"mimetype": "image/jpeg",
"destination": "uploads/",
"filename": "a3f2c1d4e5b6789012345678",
"path": "uploads/a3f2c1d4e5b6789012345678",
"size": 245760
}
Muammo: fayl nomi a3f2c1d4e5b6789012345678 — kengaytmasi yo'q. dest ishlatilganda Multer faylni shunday saqlaydi.
Storage — faylni qanday saqlash
Fayl nomini va saqlash joyini boshqarish uchun diskStorage ishlatiladi:
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/'); // ← qaerga saqlash (null = xato yo'q)
},
filename: (req, file, cb) => {
// Noyob nom: timestamp + random + asl kengaytma
const uniqueName = Date.now() + '-' + Math.round(Math.random() * 1e9);
const ext = path.extname(file.originalname); // ← .jpg, .png, .pdf
cb(null, uniqueName + ext); // ← masalan: 1717123456789-123456789.jpg
}
});
const upload = multer({ storage });
Nima uchun asl nom ishlatilmaydi? Chunki ikki foydalanuvchi foto.jpg yuborsa — biri ikkinchisini ustiga yozib ketadi. Noyob nom bu muammoni hal qiladi.
Fayl turi filtri (fileFilter)
Har qanday fayl qabul qilinmasligi kerak. Faqat ruxsat etilgan turlarni o'tkazib yuboring:
const imageFilter = (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/png', 'image/webp'];
if (allowedTypes.includes(file.mimetype)) {
cb(null, true); // ← qabul qil
} else {
cb(new Error('Faqat JPEG, PNG, WebP rasmlarga ruxsat'), false); // ← rad et
}
};
const upload = multer({
storage,
fileFilter: imageFilter,
});
Nima uchun kengaytmaga emas, mimetype ga qaraladi? Chunki tajovuzkor zararli.exe ni rasm.jpg deb nomlashi mumkin. mimetype esa fayl ichidagi ma'lumot asosida aniqlanadi — aldab bo'lmaydi.
Hajm cheklovi (size limit)
const upload = multer({
storage,
fileFilter: imageFilter,
limits: {
fileSize: 5 * 1024 * 1024, // ← 5 MB (baytda)
files: 5, // ← bir vaqtda maksimal 5 ta fayl
}
});
To'liq sozlangan misol
const express = require('express');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const app = express();
// uploads/ papkasi yo'q bo'lsa yaratish
if (!fs.existsSync('uploads')) {
fs.mkdirSync('uploads');
}
// Storage
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/');
},
filename: (req, file, cb) => {
const uniqueName = Date.now() + '-' + Math.round(Math.random() * 1e9);
const ext = path.extname(file.originalname);
cb(null, uniqueName + ext);
}
});
// Filter
const imageFilter = (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/png', 'image/webp'];
if (allowedTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error('Faqat rasm fayllarga ruxsat (JPEG, PNG, WebP)'), false);
}
};
// Upload middleware
const upload = multer({
storage,
fileFilter: imageFilter,
limits: { fileSize: 5 * 1024 * 1024 } // ← 5 MB
});
// Statik fayllar uchun
app.use('/uploads', express.static('uploads')); // ← brauzerdan ko'rish mumkin
// Bitta fayl — profil rasmi
app.post('/profil/rasm', upload.single('rasm'), (req, res) => {
if (!req.file) {
return res.status(400).json({ error: 'Fayl yuborilmadi' });
}
const fileUrl = `/uploads/${req.file.filename}`;
res.json({
message: 'Rasm yuklandi',
url: fileUrl,
hajm: req.file.size,
});
});
// Xatolarni ushlash
app.use((err, req, res, next) => {
if (err instanceof multer.MulterError) {
if (err.code === 'LIMIT_FILE_SIZE') {
return res.status(400).json({ error: 'Fayl hajmi 5 MB dan oshmasligi kerak' });
}
}
if (err.message) {
return res.status(400).json({ error: err.message });
}
next(err);
});
app.listen(3000);
Bir nechta fayl yuklash
// Bir fielddan bir nechta fayl
app.post('/galereya', upload.array('rasmlar', 10), (req, res) => {
// ↑ field nomi ↑ maksimal son
console.log(req.files); // ← fayllar massivi
const urls = req.files.map(f => `/uploads/${f.filename}`);
res.json({ rasmlar: urls });
});
// Turli fieldlardan fayllar
app.post('/mahsulot', upload.fields([
{ name: 'asosiy_rasm', maxCount: 1 },
{ name: 'qoshimcha_rasmlar', maxCount: 5 },
]), (req, res) => {
const asosiy = req.files['asosiy_rasm']?.[0];
const qoshimcha = req.files['qoshimcha_rasmlar'] || [];
res.json({ asosiy: asosiy?.filename, qoshimcha: qoshimcha.map(f => f.filename) });
});
Fayl yuboradigan frontend
<!-- HTML forma -->
<form action="/profil/rasm" method="POST" enctype="multipart/form-data">
<!-- ↑ bu majburiy -->
<input type="file" name="rasm" accept="image/*">
<button type="submit">Yuklash</button>
</form>
JavaScript bilan:
const formData = new FormData();
formData.append('rasm', fileInput.files[0]); // ← fayl qo'shiladi
const response = await fetch('/profil/rasm', {
method: 'POST',
body: formData,
// Content-Type ni O'ZINGIZ QO'YMASLIK KERAK
// Brauzer 'multipart/form-data; boundary=...' ni o'zi qo'yadi
});
Memory storage — diskga emas, xotiraga saqlash
Ba'zan faylni diskka saqlamay, to'g'ridan-to'g'RI qayta ishlash kerak (masalan Sharp bilan o'lchamni o'zgartirish):
const upload = multer({
storage: multer.memoryStorage(), // ← disk o'rniga xotira
limits: { fileSize: 5 * 1024 * 1024 }
});
app.post('/rasm-qayta-ishlash', upload.single('rasm'), async (req, res) => {
// req.file.buffer — fayl ikkilik ma'lumoti sifatida xotirada
const buffer = req.file.buffer;
// Keyingi darsda: Sharp bilan resize qilish
// const resized = await sharp(buffer).resize(300, 300).toBuffer();
});
Tez-tez uchraydigan xatolar
1. enctype="multipart/form-data" unutilgan
<!-- ❌ -->
<form action="/upload" method="POST">
<input type="file" name="rasm">
</form>
Sababi: enctype bo'lmasa brauzer faylni yubormasdan faqat fayl nomini matn sifatida yuboradi. req.file undefined bo'ladi.
2. Multer xatolarini ushlash
// ❌ — Multer xatolari oddiy error handler ga tushmaydi
app.use((err, req, res, next) => {
res.status(500).json({ error: err.message });
});
// ✅ — multer.MulterError ni alohida tekshirish kerak
app.use((err, req, res, next) => {
if (err instanceof multer.MulterError) {
// LIMIT_FILE_SIZE, LIMIT_FILE_COUNT, LIMIT_UNEXPECTED_FILE
return res.status(400).json({ error: err.message });
}
next(err);
});
Sababi: Multer o'zining MulterError classini ishlatadi — oddiy Error dan farqli.
3. uploads/ papkasini Git ga qo'shish
# .gitignore
uploads/
Sababi: foydalanuvchi fayllari Git da saqlanmasligi kerak. Repo hajmi kattalashadi, maxfiy fayllar ochiq bo'lib qolishi mumkin.
4. Fayl URL ni to'g'ri shakllanmaslik
// ❌ — path separator tizimga qarab farqlanadi
const url = 'uploads\\' + req.file.filename; // Windows da \ bo'ladi
// ✅ — path.join yoki slash bilan
const url = `/uploads/${req.file.filename}`;
Xulosa
- Multer —
multipart/form-datani qayta ishlaydi, fayllarnireq.file/req.filesga qo'yadi diskStorage— fayl nomi va saqlash joyini boshqaradifileFilter— fayl turinimimetypeorqali tekshiradi (kengaytmaga ishonmang)limits.fileSize— hajm cheklovini baytda belgilaydimulter.memoryStorage()— faylni diskka emas, xotiraga saqlaydi (keyingi qayta ishlash uchun)
Keyingi dars: 33-dars: Sharp — rasmlarni qayta ishlash, resize, compress