5.11-bob: File upload — Multer (lokal va cloud)
5-QISM — Node.js Backend · 11-mavzu
1. Kirish va motivatsiya
Backend infratuzilmasini (server, REST, validatsiya, error — 5.5–5.10) qurdik. Endi deyarli har bir real ilovada uchraydigan amaliy vazifani — fayl yuklashni — o'rganamiz: profil rasmi (avatar), mahsulot suratlari (e-commerce), hujjatlar (PDF), import fayllari (CSV — 5.4).
Fayl yuklash — oddiy JSON so'rovidan (5.6) boshqacha. JSON — matn (0.1, 2.8); fayl — binary ma'lumot (rasm, video — 0.1: bayt), va u multipart/form-data degan maxsus format bilan yuboriladi (0.4, 1.1: HTML form). express.json() 5.6-bob buni parse qila olmaydi — maxsus vosita kerak: Multer.
Multer — Express uchun multipart/form-datani qayta ishlovchi middleware (expressjs.com). U fayllarni qabul qiladi, saqlaydi (disk yoki xotira), req.file/req.filesga qo'yadi. Lekin fayl yuklash — xavfsizlikning eng nozik nuqtalaridan biri (14): hacker zararli fayl (virus, ulkan fayl — DoS, soxta tur) yuborishi mumkin. Shuning uchun tur, hajm, nom tekshiruvi — majburiy.
O'xshatish: fayl yuklash — pochta orqali posilka qabul qilish. Multer — pochta xodimi: posilkani qabul qiladi, og'irligini tekshiradi (hajm limit), ichida nima borligini ko'radi (fayl turi), omborga joylaydi (disk/cloud), va sizga xabar beradi (
req.file). Tekshiruvsiz har xil xavfli posilka (bomba — virus) keladi (14).
Nega muhim?
- Deyarli har ilova — avatar, rasm, hujjat yuklash.
- Xavfsizlik (14) — fayl yuklash — eng ko'p hujum qilinadigan nuqta (zararli fayl, DoS, path traversal).
- Cloud (10.6) — production'da fayllar S3/Cloudinary'da (server diskida emas).
- Stream/binary (5.4, 0.1) — fayl — binary; katta fayl — xotira muammosi.
2. Nazariya — chuqur tushuntirish
2.1. multipart/form-data — nega fayl uchun maxsus format
HTTP body 0.4-bob odatda JSON (matn — 2.8). Lekin fayl — binary (rasm baytlari — 0.1) + boshqa matn maydonlari (masalan rasm bilan birga "tavsif"). Ularni birga yuborish uchun multipart/form-data (0.4: Content-Type) ishlatiladi:
Content-Type: multipart/form-data; boundary=----xyz
------xyz
Content-Disposition: form-data; name="tavsif"
Mening rasmim
------xyz
Content-Disposition: form-data; name="rasm"; filename="surat.jpg"
Content-Type: image/jpeg
<binary rasm baytlari> fayl 0.1-bob
------xyz--
express.json()5.6-bob buni parse qila olmaydi — u faqat JSON uchun.multipart/form-datauchun Multer kerak. HTML form'daenctype="multipart/form-data"1.1-bob bo'lishi shart.
2.2. Multer nima va o'rnatish
npm install multer # (5.2)import multer from "multer";
const upload = multer({ dest: "uploads/" }); // oddiy (disk'ga saqlaydi)Multer — middleware 5.6-bob: u so'rovdagi faylni parse qiladi va req.file (bitta) yoki req.files (ko'p), matn maydonlarini esa req.bodyga qo'yadi.
2.3. Saqlash usullari: diskStorage vs memoryStorage
Multer ikki saqlash usuli beradi (multer docs):
diskStorage — faylni DISKKA yozadi 0.2-bob; req.file.path bo'ladi
lokal saqlash, katta fayl (xotira tejaydi — 0.1)
memoryStorage — faylni XOTIRADA Buffer 0.1-bob sifatida; req.file.buffer bo'ladi
cloud (S3) ga uzatish, qayta ishlash (resize); kichik faylQaysi: diskStorage — lokal saqlash, katta fayl (xotira-samarali — 0.1). memoryStorage — faylni cloud'ga uzatish (S3 — 2.11) yoki qayta ishlash (rasm resize) kerak bo'lganda; lekin xotirada (katta/ko'p fayl — RAM portlaydi — 0.1, ehtiyot).
2.4. diskStorage — sozlash (papka, nom)
import multer from "multer";
import path from "node:path"; // (5.3)
import crypto from "node:crypto"; // (5.3)
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "uploads/"); // papka (2.3)
},
filename: (req, file, cb) => {
// NOYOB nom — original nomni ishlatmang (xavfsizlik + konflikt — 2.9, 14)
const noyob = crypto.randomBytes(16).toString("hex"); // (5.3)
const kengaytma = path.extname(file.originalname); // (5.3: .jpg)
cb(null, `${noyob}${kengaytma}`); // masalan "a3f5...c1.jpg"
},
});
const upload = multer({ storage });Original fayl nomini ishlatmang (
file.originalnameto'g'ridan): (1) konflikt (ikki "rasm.jpg" bir-birini bosadi); (2) xavfsizlik — path traversal (../../etc/passwd— 14). Noyob nom yarating (crypto — 5.3).
2.5. Bitta/ko'p fayl: single, array, fields
upload.single("rasm") // bitta fayl req.file
upload.array("rasmlar", 5) // ko'p (max 5) req.files (massiv)
upload.fields([ // turli maydonlar
{ name: "avatar", maxCount: 1 },
{ name: "galereya", maxCount: 8 },
]) // req.files.avatar, req.files.galereya
upload.none() // fayl yo'q, faqat matn (multipart)2.6. req.file obyekti
req.file = {
fieldname: "rasm", // form maydon nomi
originalname: "surat.jpg", // foydalanuvchi fayl nomi (ISHONMA — 2.4)
mimetype: "image/jpeg", // MIME tur (SPOOF qilinishi mumkin — 2.8, 14)
size: 102400, // bayt 0.1-bob — limit (2.7)
// diskStorage:
path: "uploads/a3f5.jpg", // saqlangan yo'l (DB'ga shu — 2.12)
filename: "a3f5.jpg",
// memoryStorage:
buffer: <Buffer ...>, // xotiradagi fayl (0.1, 2.3)
};2.7. limits — hajm va son chegarasi (DoS himoyasi — 14)
Limit MAJBURIY (multer docs, 14): chegarasiz, hacker ulkan fayl yuborib serverni to'ldiradi (DoS):
const upload = multer({
storage,
limits: {
fileSize: 5 * 1024 * 1024, // 5 MB (0.1: bayt) — eng muhim
files: 5, // max 5 fayl
fields: 10, // max matn maydon
},
});
// Chegaradan oshsa MulterError: "File too large" (2.10)2.8. fileFilter — tur tekshiruvi (xavfsizlik — 14)
fileFilter — qaysi fayl qabul qilinishini boshqaradi (faqat rasm, faqat PDF):
const fileFilter = (req, file, cb) => {
const ruxsat = ["image/jpeg", "image/png", "image/webp"]; // allowlist (14)
if (ruxsat.includes(file.mimetype)) {
cb(null, true); // qabul qiling
} else {
cb(new Error("Faqat rasm (JPEG/PNG/WebP)"), false); // rad eting (2.10)
}
};
const upload = multer({ storage, fileFilter, limits: {...} });MIME tur SPOOF qilinishi mumkin (14):
file.mimetype— client yuboradi (Content-Type— 2.1); hacker uni soxtalashtirib,.exeniimage/jpegdeb yuborishi mumkin. Ishonchli tekshiruv — magic bytes (faylning dastlabki baytlari — 0.1, 2.9-JS):file-typepaketi yoki qo'lda. Kengaytma + MIME — birinchi qadam; magic bytes — haqiqiy himoya (14).
2.9. Noyob fayl nomi (konflikt + xavfsizlik — 2.4)
filename: (req, file, cb) => {
const noyob = `${Date.now()}-${crypto.randomUUID()}`; // (5.3: UUID)
cb(null, `${noyob}${path.extname(file.originalname)}`);
}Noyob nom: (1) konfliktni oldini oladi; (2) original nomdagi xavfli belgilarni (path traversal — 14) yo'qotadi; (3) fayl nomidan ma'lumot oshkor bo'lmaydi.
2.10. Multer xatolarini boshqarish (5.10)
Multer xatolari (MulterError) — limit oshishi, noto'g'ri fayl — error handler'da 5.10-bob ushlanadi:
import multer from "multer";
app.use((err, req, res, next) => {
if (err instanceof multer.MulterError) { // Multer xatosi (5.10)
if (err.code === "LIMIT_FILE_SIZE") {
return res.status(400).json({ error: "Fayl juda katta (max 5MB)" }); // (5.7: 400)
}
return res.status(400).json({ error: err.message });
}
next(err); // boshqa xato — global handler (5.10)
});2.11. Cloud upload (S3, Cloudinary) — production
Production'da (10) fayllar server diskida emas, cloud'da saqlanadi (S3 — 10.6, Cloudinary):
Nega cloud: server diski cheklangan va vaqtinchalik (deploy/restart'da yo'qoladi — 10.3 Docker); cloud — cheksiz, CDN (tez yetkazib berish), zaxira, masshtab 9.9-bob.
import multer from "multer";
import multerS3 from "multer-s3"; // npm install multer-s3 @aws-sdk/client-s3
import { S3Client } from "@aws-sdk/client-s3";
const s3 = new S3Client({ region: process.env.AWS_REGION }); // (5.8: env)
const upload = multer({
storage: multerS3({
s3,
bucket: process.env.S3_BUCKET,
key: (req, file, cb) => {
cb(null, `avatars/${crypto.randomUUID()}${path.extname(file.originalname)}`); // (2.9)
},
}),
limits: { fileSize: 5 * 1024 * 1024 }, // (2.7)
fileFilter, // (2.8)
});
// req.file.location S3 URL (DB'ga shu — 2.12)Muqobil — memoryStorage + qo'lda upload:
memoryStorage2.3-bob bilan faylnireq.file.bufferga olib, keyin SDK bilan S3/Cloudinary'ga qo'lda yuklash (ko'proq nazorat — resize qilib yuklash).
2.12. Faylni DB bilan bog'lash (6)
Faylning O'ZINI DB'ga saqlamang (6) — faqat yo'l/URLni:
// DB'ga: fayl yo'li/URL (faylning o'zi emas — 6)
await User.update({ avatar: req.file.path }); // diskStorage: yo'l
await User.update({ avatar: req.file.location }); // S3: URL (2.11)
// Fayl — diskda/cloud'da; DB'da faqat manzil (kichik, tez)Nega: fayl (rasm — MB) DB'da (6) — DB'ni shishiradi, sekinlashtiradi. Fayl — fayl tizimida/cloud'da; DB'da faqat manzil (string). Bu — standart naqsh.
2.13. Yuklangan faylni ko'rsatish (static — 5.6)
Lokal saqlangan faylni brauzerga ko'rsatish (5.6: static):
app.use("/uploads", express.static("uploads")); // (5.6)
// uploads/a3f5.jpg http://localhost:3000/uploads/a3f5.jpgCloud (S3 — 2.11) bo'lsa — to'g'ridan URL (CDN); static kerak emas.
Yuklab olish / nazoratli xizmat ko'rsatish (stream — 5.4): static — public fayllar uchun qulay. Lekin fayl maxfiy bo'lsa (faqat egasi ko'rishi kerak) yoki "Yuklab olish" tugmasi kerak bo'lsa, faylni alohida route'da — avval ruxsatni tekshirib, keyin uzatib bering. Katta fayl uchun butun faylni xotiraga olmang — stream qiling (5.4: katta fayl — xotira muammosi):
import { createReadStream } from "node:fs"; // (5.3, 5.4)
app.get("/files/:nom", auth, (req, res) => { // auth — ruxsat (14)
const nom = path.basename(req.params.nom); // path traversal'dan himoya (2.4, 14)
res.download(`uploads/${nom}`); // "Yuklab olish" (Content-Disposition)
// yoki nazoratli stream: createReadStream(...).pipe(res) (5.4)
});Route orqali xizmat ko'rsatganda ham fayl nomini
path.basenamebilan tozalang —../../(path traversal — 14) bilan ilova tashqarisidagi fayl o'qilishining oldini oladi.
2.14. Xavfsizlik xulosasi (14)
Fayl yuklash xavfsizligi (eng nozik nuqta — 14):
- Hajm limit (DoS — 2.7).
- Tur tekshiruvi — MIME + magic bytes (spoof — 2.8).
- Noyob nom (path traversal — 2.4, 2.9).
- Alohida papka/bucket — ilova kodidan tashqarida 2.4-bob.
- Bajariladigan fayl rad eting (
.exe,.sh— 14). - Cloud'da public/private to'g'ri sozlang (maxfiy fayl — 2.11).
- Antivirus (jiddiy loyihada) — yuklangan faylni skanerlash.
2.15. Rasm qayta ishlash (sharp — qisqacha)
Yuklangan rasmni resize/optimallash (xotira/CDN tejaydi) — sharp paketi (memoryStorage bilan):
import sharp from "sharp"; // npm install sharp
// memoryStorage'dan buffer (2.3)
const optimal = await sharp(req.file.buffer)
.resize(800, 800, { fit: "inside" }) // o'lcham cheklash
.webp({ quality: 80 }) // WebP (kichik — 0.1)
.toBuffer();
// keyin optimal'ni saqlash/S3'ga3. Sintaksis — tez ma'lumotnoma
import multer from "multer";
// Saqlash (2.3)
multer.diskStorage({ destination, filename }) // disk
multer.memoryStorage() // xotira (cloud uchun)
// Sozlash (2.7, 2.8)
const upload = multer({ storage, limits: { fileSize, files }, fileFilter });
// Middleware (2.5)
upload.single("field") upload.array("field", n) upload.fields([...])
// req (2.6)
req.file // bitta (path/buffer/mimetype/size)
req.files // ko'p
req.body // matn maydonlar
// Xato 2.10-bob: err instanceof multer.MulterError4. Batafsil kod namunalari
Misol 1 — Oddiy avatar yuklash (disk — 2.4, 2.5)
import express from "express";
import multer from "multer";
import path from "node:path";
import crypto from "node:crypto"; // (5.3)
const app = express();
const storage = multer.diskStorage({
destination: (req, file, cb) => cb(null, "uploads/avatars/"), // (2.4)
filename: (req, file, cb) => {
const noyob = crypto.randomUUID(); // (5.3, 2.9)
cb(null, `${noyob}${path.extname(file.originalname)}`);
},
});
const upload = multer({
storage,
limits: { fileSize: 2 * 1024 * 1024 }, // 2 MB (2.7)
fileFilter: (req, file, cb) => { // (2.8)
const ok = ["image/jpeg", "image/png", "image/webp"].includes(file.mimetype);
cb(ok ? null : new Error("Faqat rasm"), ok);
},
});
// upload.single — middleware sifatida (2.5, 5.6)
app.post("/avatar", upload.single("avatar"), (req, res) => {
if (!req.file) return res.status(400).json({ error: "Fayl kerak" }); // (5.7)
res.json({ success: true, path: req.file.path, size: req.file.size }); // (2.6)
});Misol 2 — Ko'p fayl (galereya — 2.5)
// Max 8 ta rasm (2.5, 2.7)
app.post("/gallery", upload.array("images", 8), (req, res) => {
if (!req.files?.length) return res.status(400).json({ error: "Rasm kerak" });
const yollar = req.files.map((f) => f.path); // (2.6, 2.7-JS: map)
res.json({ success: true, count: req.files.length, files: yollar });
});
// Turli maydonlar (2.5)
const cpUpload = upload.fields([
{ name: "avatar", maxCount: 1 },
{ name: "hujjatlar", maxCount: 5 },
]);
app.post("/profile", cpUpload, (req, res) => {
res.json({
avatar: req.files.avatar?.[0]?.path, // (2.6)
hujjatlar: req.files.hujjatlar?.map((f) => f.path),
});
});Misol 3 — Multer xato handler (2.10, 5.10)
import multer from "multer";
// Multer xatolarini boshqarish (5.10: error handler)
const uploadErrorHandler = (err, req, res, next) => {
if (err instanceof multer.MulterError) { // (2.10)
const xabarlar = {
LIMIT_FILE_SIZE: "Fayl juda katta (max 2MB)",
LIMIT_FILE_COUNT: "Juda ko'p fayl",
LIMIT_UNEXPECTED_FILE: "Kutilmagan maydon",
};
return res.status(400).json({ error: xabarlar[err.code] || err.message }); // (5.7)
}
if (err) return res.status(400).json({ error: err.message }); // fileFilter xatosi (2.8)
next();
};
app.post("/upload", upload.single("file"), uploadErrorHandler, (req, res) => {
res.json({ success: true, file: req.file.filename });
});Misol 4 — Magic bytes bilan ishonchli tur tekshiruvi (2.8, 14)
import { fileTypeFromBuffer } from "file-type"; // npm install file-type
import multer from "multer";
const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 5e6 } }); // (2.3)
app.post("/secure-upload", upload.single("file"), async (req, res, next) => {
// MIME tur (req.file.mimetype) SPOOF qilinishi mumkin (2.8, 14)
// Magic bytes (haqiqiy tur — fayl baytlaridan — 0.1)
const turi = await fileTypeFromBuffer(req.file.buffer); // (2.3: buffer)
const ruxsat = ["image/jpeg", "image/png", "image/webp"];
if (!turi || !ruxsat.includes(turi.mime)) {
return res.status(400).json({ error: "Fayl haqiqatan rasm emas" }); // (14)
}
// Endi ishonchli — faylni saqlang/yuklang
res.json({ success: true, haqiqiyTur: turi.mime });
});Misol 5 — S3 cloud upload (2.11)
import multer from "multer";
import multerS3 from "multer-s3";
import { S3Client } from "@aws-sdk/client-s3";
import crypto from "node:crypto";
import path from "node:path";
import { config } from "../config/index.js"; // (5.8)
const s3 = new S3Client({ region: config.aws.region });
const uploadS3 = multer({
storage: multerS3({
s3,
bucket: config.aws.bucket,
contentType: multerS3.AUTO_CONTENT_TYPE,
key: (req, file, cb) => {
cb(null, `uploads/${crypto.randomUUID()}${path.extname(file.originalname)}`); // (2.9)
},
}),
limits: { fileSize: 5 * 1024 * 1024 }, // (2.7)
fileFilter: (req, file, cb) => // (2.8)
cb(null, ["image/jpeg", "image/png"].includes(file.mimetype)),
});
app.post("/s3-upload", uploadS3.single("image"), (req, res) => {
res.json({ success: true, url: req.file.location }); // S3 URL (2.11)
});Misol 6 — Rasm optimizatsiya (sharp — 2.15)
import multer from "multer";
import sharp from "sharp"; // npm install sharp
import crypto from "node:crypto";
import { writeFile } from "node:fs/promises"; // (5.3)
const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 10e6 } }); // (2.3)
app.post("/optimized-avatar", upload.single("avatar"), async (req, res, next) => {
try {
const nom = `${crypto.randomUUID()}.webp`;
const optimal = await sharp(req.file.buffer) // buffer (2.3)
.resize(500, 500, { fit: "cover" }) // kvadrat 500px (2.15)
.webp({ quality: 80 }) // WebP, 80% (0.1: siqish)
.toBuffer();
await writeFile(`uploads/${nom}`, optimal); // saqlang (5.3)
res.json({ success: true, file: nom, size: optimal.length }); // kichikroq!
} catch (err) {
next(err); // (5.10)
}
});Misol 7 — To'liq: yuklash + DB + ko'rsatish (2.12, 2.13)
import express from "express";
const app = express();
// Yuklangan fayllarni ko'rsatish (static — 2.13, 5.6)
app.use("/uploads", express.static("uploads"));
// Avatar yuklash + DB'ga yo'l saqlash (2.12)
app.post("/users/:id/avatar", upload.single("avatar"), async (req, res, next) => {
try {
if (!req.file) return res.status(400).json({ error: "Fayl kerak" });
const url = `/uploads/avatars/${req.file.filename}`; // (2.13)
await User.update(req.params.id, { avatar: url }); // DB: faqat URL (2.12, 6)
res.json({ success: true, avatar: url });
} catch (err) {
next(err); // (5.10)
}
});
// Brauzerda: http://localhost:3000/uploads/avatars/a3f5.jpg5. To'g'ri va noto'g'ri holatlar
1) Hajm limitisiz
// chegarasiz DoS (ulkan fayl — 14, 2.7)
const upload = multer({ storage });
// limit
const upload = multer({ storage, limits: { fileSize: 5e6 } });2) Original fayl nomini ishlatish
// konflikt + path traversal (14, 2.4)
filename: (req, file, cb) => cb(null, file.originalname);
// noyob nom (crypto)
filename: (req, file, cb) => cb(null, `${crypto.randomUUID()}${path.extname(file.originalname)}`);3) Faqat MIME turga ishonish
// MIME spoof qilinadi (.exe image/jpeg — 14, 2.8)
if (file.mimetype === "image/jpeg") {...}
// magic bytes (file-type — Misol 4)
const turi = await fileTypeFromBuffer(buffer);4) Faylning o'zini DB'ga saqlash
// rasm (MB) DB'da — DB shishadi (6, 2.12)
await User.update({ avatar: req.file.buffer });
// faqat yo'l/URL
await User.update({ avatar: req.file.path });5) memoryStorage bilan katta/ko'p fayl
memoryStorage + 1GB fayl RAM portlaydi (0.1, 2.3)
katta fayl — diskStorage; memoryStorage faqat kichik/cloud uchun6. Keng tarqalgan xatolar va yechimlari
Xato 1 — req.file undefined
Sababi: form enctype="multipart/form-data" emas 2.1-bob, yoki maydon nomi mos emas (upload.single("avatar") vs form name). Yechimi: form enctype; maydon nomini moslash.
Xato 2 — MulterError: File too large
Sababi: fayl limits.fileSizedan katta 2.7-bob. Yechimi: limit'ni oshiring yoki foydalanuvchiga aniq xabar 2.10-bob; frontend'da ham tekshiring (UX).
Xato 3 — LIMIT_UNEXPECTED_FILE
Sababi: form maydon nomi upload.single("x")dagi nomga mos emas 2.5-bob. Yechimi: nomlarni moslash.
Xato 4 — Yuklangan fayl ko'rinmaydi (404)
Sababi: static middleware yo'q 2.13-bob. Yechimi: app.use("/uploads", express.static("uploads")) 5.6-bob.
Xato 5 — Production'da fayllar yo'qoladi
Sababi: server diskida saqlangan; deploy/restart (Docker — 10.3) diskni tozalaydi. Yechimi: cloud (S3 — 2.11).
Xato 6 — Zararli fayl yuklandi
Sababi: faqat MIME/kengaytma tekshiruvi (spoof — 2.8, 14). Yechimi: magic bytes (Misol 4); bajariladigan faylni rad eting; alohida bucket.
7. Integratsiya — bu mavzu stack'ning qayerida uchraydi
- Express 5.6-bob: Multer — middleware.
- multipart/form-data (0.4, 1.1): HTML form, HTTP body.
- Stream/Buffer (5.4, 0.1): fayl — binary; diskStorage stream ustida.
- crypto 5.3-bob: noyob nom (UUID).
- Validatsiya 5.9-bob: fayl tur/hajm — validatsiyaning bir qismi.
- Error handling 5.10-bob: MulterError.
- DB (6): fayl yo'li/URL saqlash.
- Cloud 10.6-bob: S3; CDN.
- Xavfsizlik (14): eng nozik nuqta.
- NestJS 8.8-bob:
FileInterceptor(Multer ustida).
8. Eng yaxshi amaliyotlar (best practices)
- Hajm limit MAJBURIY (
limits.fileSize— DoS — 2.7, 14). - Tur tekshiruvi —
fileFilter(MIME) + magic bytes (spoof — 2.8, Misol 4). - Noyob fayl nomi (crypto — original nomni ishlatmang — 2.4, 2.9).
- Faqat yo'l/URL'ni DB'ga, faylning o'zini emas 2.12-bob.
- Production'da cloud (S3/Cloudinary — diskda emas — 2.11, 10.6).
- memoryStorage faqat kichik/cloud/qayta ishlash uchun (katta — disk — 2.3).
- Alohida papka/bucket (ilova kodidan tashqarida — 2.4, 14).
- MulterError'ni boshqaring (aniq xabar — 2.10, 5.10).
- Rasmni optimallashtiring (sharp — resize/WebP — 2.15) — xotira/CDN tejaydi.
- Frontend'da ham tekshiring (UX), lekin backend — haqiqiy himoya (5.9, 14).
9. Amaliy loyiha: "Xavfsiz Fayl Yuklash Tizimi"
Fayl yuklashni xavfsiz va professional darajada mustahkamlash.
Maqsad
Multer bilan disk/cloud saqlash, tur/hajm validatsiyasi, magic bytes, noyob nom va DB integratsiyasini birlashtirib, xavfsiz fayl yuklash tizimini qurish.
Talablar (requirements)
- Avatar yuklash:
upload.single— disk'ga, noyob nom (crypto), tur (rasm) + hajm (2MB) limit (Misol 1, 2.4, 2.7, 2.8). - Ko'p rasm (galereya):
upload.array(max 8) yokifields(avatar + galereya — Misol 2, 2.5). - Magic bytes:
file-typebilan haqiqiy tur tekshiruvi (MIME spoof'dan himoya — Misol 4, 14). - Multer xato handler: LIMIT_FILE_SIZE va boshqalarga aniq xabar (Misol 3, 5.10).
- DB integratsiya: fayl yo'li/URL'ni saqlash (faylning o'zi emas — 2.12).
- Static ko'rsatish: yuklangan faylni brauzerda ochish 2.13-bob.
- Rasm optimizatsiya (bonus): sharp bilan resize + WebP (Misol 6, 2.15).
- Cloud (bonus): S3 yoki Cloudinary'ga yuklash (memoryStorage — Misol 5, 2.11).
- Xavfsizlik: limit + tur + magic bytes + noyob nom + alohida papka (2.14, 14).
Maslahatlar (hint)
- Form
enctype="multipart/form-data"(1.1, 1-xato); maydon nomiupload.single("x")ga mos. - Noyob nom:
crypto.randomUUID() + path.extname(originalname)(2.9, 5.3). - Magic bytes:
fileTypeFromBuffer(req.file.buffer)(memoryStorage — Misol 4). - Limit:
limits: { fileSize: 2e6 }2.7-bob. - DB: faqat
req.file.path/req.file.location2.12-bob. - MulterError:
err instanceof multer.MulterError2.10-bob.
"Tayyor" mezonlari (acceptance criteria)
- Avatar yuklash (noyob nom, tur+hajm limit) ishlaydi.
- Ko'p fayl (array/fields) ishlaydi.
- Magic bytes bilan haqiqiy tur tekshiriladi (spoof rad).
- Multer xatolari aniq xabar bilan boshqariladi.
- DB'ga faqat yo'l/URL saqlanadi.
- Yuklangan fayl static orqali ko'rinadi.
- (Bonus) sharp optimizatsiya yoki S3.
- Hajm/tur/nom xavfsizligi ta'minlangan.
Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.
10. Xulosa va keyingi bobga ko'prik
Bu bobda amaliy, xavfsizlik-nozik vazifani — fayl yuklashni o'rgandik:
multipart/form-data0.4-bob — fayl uchun maxsus format;express.json()parse qilolmaydi Multer.- Saqlash: diskStorage (disk, katta fayl) vs memoryStorage (Buffer, cloud/qayta ishlash).
- single/array/fields;
req.file/req.files; limits (hajm/son — DoS — 14); fileFilter (tur). - Xavfsizlik (14): noyob nom (path traversal — 2.4), magic bytes (MIME spoof — 2.8), alohida papka.
- Cloud (S3/Cloudinary — production — 2.11); DB'ga faqat yo'l/URL 2.12-bob; static ko'rsatish 2.13-bob; sharp optimizatsiya.
Keyingi bob — 5.12-bob: Logger — Winston / Pino (chuqur). Error handling 5.10-bob da xatolarni log qilishni tilga oldik; endi professional loggingni — Winston/Pino bilan strukturali log, darajalar (level), transport (fayl/konsol), production amaliyoti — chuqur o'rganamiz. Log — production'da (10) "ko'z"ingiz; usiz nima bo'layotganini bilmaysiz.
Foydalanilgan rasmiy/ishonchli manbalar
- expressjs.com/resources/middleware/multer; github.com/expressjs/multer
- Multer docs — diskStorage/memoryStorage, limits, fileFilter
- multer-s3, file-type, sharp (cloud, magic bytes, optimizatsiya)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!