WisarWisar
Dasturlash kitobi/5-QISM — Nodejs17 daqiqa

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:

text
  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-data uchun Multer kerak. HTML form'da enctype="multipart/form-data" 1.1-bob bo'lishi shart.

2.2. Multer nima va o'rnatish

bash
npm install multer          # (5.2)
js
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):

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

Qaysi: 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)

js
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.originalname to'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

js
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

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

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

js
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, .exeni image/jpeg deb yuborishi mumkin. Ishonchli tekshiruv — magic bytes (faylning dastlabki baytlari — 0.1, 2.9-JS): file-type paketi yoki qo'lda. Kengaytma + MIME — birinchi qadam; magic bytes — haqiqiy himoya (14).

2.9. Noyob fayl nomi (konflikt + xavfsizlik — 2.4)

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

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

js
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: memoryStorage 2.3-bob bilan faylni req.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:

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

js
app.use("/uploads", express.static("uploads"));   // (5.6)
// uploads/a3f5.jpg  http://localhost:3000/uploads/a3f5.jpg

Cloud (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):

js
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.basename bilan 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):

js
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'ga

3. Sintaksis — tez ma'lumotnoma

js
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.MulterError

4. Batafsil kod namunalari

Misol 1 — Oddiy avatar yuklash (disk — 2.4, 2.5)

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

js
// 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)

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

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

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

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

js
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.jpg

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

1) Hajm limitisiz

js
//  chegarasiz  DoS (ulkan fayl — 14, 2.7)
const upload = multer({ storage });

//  limit
const upload = multer({ storage, limits: { fileSize: 5e6 } });

2) Original fayl nomini ishlatish

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

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

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

text
 memoryStorage + 1GB fayl  RAM portlaydi (0.1, 2.3)
 katta fayl — diskStorage; memoryStorage faqat kichik/cloud uchun

6. 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 tekshiruvifileFilter (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)

  1. Avatar yuklash: upload.single — disk'ga, noyob nom (crypto), tur (rasm) + hajm (2MB) limit (Misol 1, 2.4, 2.7, 2.8).
  2. Ko'p rasm (galereya): upload.array (max 8) yoki fields (avatar + galereya — Misol 2, 2.5).
  3. Magic bytes: file-type bilan haqiqiy tur tekshiruvi (MIME spoof'dan himoya — Misol 4, 14).
  4. Multer xato handler: LIMIT_FILE_SIZE va boshqalarga aniq xabar (Misol 3, 5.10).
  5. DB integratsiya: fayl yo'li/URL'ni saqlash (faylning o'zi emas — 2.12).
  6. Static ko'rsatish: yuklangan faylni brauzerda ochish 2.13-bob.
  7. Rasm optimizatsiya (bonus): sharp bilan resize + WebP (Misol 6, 2.15).
  8. Cloud (bonus): S3 yoki Cloudinary'ga yuklash (memoryStorage — Misol 5, 2.11).
  9. Xavfsizlik: limit + tur + magic bytes + noyob nom + alohida papka (2.14, 14).

Maslahatlar (hint)

  • Form enctype="multipart/form-data" (1.1, 1-xato); maydon nomi upload.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.location 2.12-bob.
  • MulterError: err instanceof multer.MulterError 2.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-data 0.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!
5.11-bob: File upload — Multer (lokal va cloud) — Wisar