Express: fayl yuklash

Foydalanuvchi endi tizimga kiradi. Tabiiy keyingi qadam — profil rasmini qo’yish. Lekin express.json() va express.urlencoded() fayl bilan ishlay olmaydi: ular matn kutadi, rasm esa ikkilik ma’lumot.

Bu darsda fayl qabul qilishni va uni xavfsiz saqlashni ko’ramiz.

Forma boshqa formatda yuboradi

Formada enctype atributi yozilishi shart:

<form action="/upload" method="POST" enctype="multipart/form-data">
  <p>
    <label for="avatar">Profil rasmi:</label>
    <input type="file" id="avatar" name="avatar" accept="image/*" />
  </p>
  <button type="submit">Yuklash</button>
</form>

multer

npm install multer

Eng sodda shakli:

const multer = require("multer");

const upload = multer({ dest: "uploads/" });

app.post("/upload", upload.single("avatar"), (request, response) => {
  response.json({ fileName: request.file.filename });
});

Bu ishlaydi, lekin ishlab turgan serverga yaramaydi: fayl hajmi cheklanmagan, turi tekshirilmagan, nomi tushunarsiz. Har uchalasini to’g’rilaymiz.

Fayl nomi va saqlash joyi

const path = require("path");
const crypto = require("crypto");
const multer = require("multer");

const uploadsDir = path.join(__dirname, "uploads");

const storage = multer.diskStorage({
  destination(request, file, callback) {
    callback(null, uploadsDir);
  },
  filename(request, file, callback) {
    // Kengaytmani saqlaymiz, nomni o'zimiz yaratamiz
    const extension = path.extname(file.originalname).toLowerCase();
    const randomName = crypto.randomUUID();

    callback(null, `${randomName}${extension}`);
  },
});

Bu yerdagi eng muhim qaror — fayl nomini o’zimiz berishimiz.

Asl nomni saqlash kerak bo’lsa, uni bazaga yozing: diskda tasodifiy nom, bazada original_name ustuni. Yuklab olishda brauzerga asl nomni ko’rsatasiz, disk esa tegilmaydi.

Turni tekshirish: ikki qavat

const allowedTypes = ["image/jpeg", "image/png", "image/webp"];
const allowedExtensions = [".jpg", ".jpeg", ".png", ".webp"];

function fileFilter(request, file, callback) {
  const extension = path.extname(file.originalname).toLowerCase();

  if (
    !allowedTypes.includes(file.mimetype) ||
    !allowedExtensions.includes(extension)
  ) {
    return callback(new Error("Faqat JPEG, PNG va WebP rasm yuklash mumkin"));
  }

  callback(null, true);
}

Nega ikkala tekshiruv ham kerak? Chunki ikkalasini ham mijoz yuboradi va ikkalasi ham yolg’on bo’lishi mumkin. Hujumchi .exe faylga image/png MIME turini yozib yuborishi hech gap emas.

Ikkita to’siq bittasidan yaxshiroq, lekin ular ham yetarli emas:

Hajm chegarasi

const upload = multer({
  storage: storage,
  fileFilter: fileFilter,
  limits: {
    fileSize: 2 * 1024 * 1024,  // 2 MB
    files: 1,
  },
});

Marshrut va statik berish

const express = require("express");

const app = express();

// Yuklangan fayllarni brauzerga beramiz
app.use("/uploads", express.static(uploadsDir));

app.post("/upload", upload.single("avatar"), (request, response) => {
  if (!request.file) {
    return response.status(400).json({ error: "Fayl yuborilmadi" });
  }

  response.status(201).json({
    fileName: request.file.filename,
    size: request.file.size,
    url: `/uploads/${request.file.filename}`,
  });
});

upload.single("avatar") dagi matn — formadagi name atributi. Ular mos kelmasa, request.file undefined bo’ladi.

express.static statik fayllar darsida ko’rilgan. Bu yerda u yuklangan rasmni /uploads/nom.png manzilida ochadi.

multer metodi Qachon Natija
upload.single("avatar") Bitta fayl request.file
upload.array("photos", 5) Bir nechta request.files massivi
upload.none() Faylsiz multipart forma Faqat request.body

Xatolarni ushlash

multer xatolari maxsus turda keladi va ularni ajratib ishlash kerak:

app.use((err, request, response, next) => {
  if (err instanceof multer.MulterError) {
    if (err.code === "LIMIT_FILE_SIZE") {
      return response.status(413).json({ error: "Fayl 2 MB dan katta" });
    }

    return response.status(400).json({ error: "Fayl yuklashda xato" });
  }

  if (err.message.startsWith("Faqat")) {
    return response.status(400).json({ error: err.message });
  }

  console.error(err);
  response.status(500).json({ error: "Serverda xato yuz berdi" });
});

To’rt parametrli xato middleware’i xatolarni boshqarish darsida ko’rilgan. 413 Payload Too Large — “yuborgan ma’lumotingiz juda katta” degan maxsus kod.

Sinash

curl -i -F "avatar=@test.png" http://localhost:3000/upload
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8

{"fileName":"0187982a-f1c2-450a-98b2-95684d203872.png","size":75,"url":"/uploads/0187982a-f1c2-450a-98b2-95684d203872.png"}

-F bayrog’i curl ga multipart/form-data yuborishni aytadi, @ belgisi esa “bu fayl nomi, mazmunini o’qib yubor” degani.

Matn fayli bilan:

curl -i -F "avatar=@note.txt" http://localhost:3000/upload
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8

{"error":"Faqat JPEG, PNG va WebP rasm yuklash mumkin"}

Katta fayl bilan:

curl -i -F "avatar=@big.png" http://localhost:3000/upload
HTTP/1.1 413 Payload Too Large
Content-Type: application/json; charset=utf-8

{"error":"Fayl 2 MB dan katta"}

Faylsiz:

curl -i -X POST http://localhost:3000/upload
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8

{"error":"Fayl yuborilmadi"}

To’rt holat ham to’g’ri javob berdi. Yuklangan rasmni brauzerda http://localhost:3000/uploads/<fayl-nomi> manzilida ochib ko’ring.

Faylni foydalanuvchiga bog’lash

Fayl diskda, endi uni bazaga yozamiz. Migratsiya darsidagi qolipda:

// migrations/005-add-user-avatar.js
module.exports = {
  name: "005-add-user-avatar",
  up(database) {
    database.exec("ALTER TABLE users ADD COLUMN avatar_file TEXT");
  },
};

Ustun paydo bo’ldi, endi unga yozadigan funksiya kerak. Model qatlami darsidagi qoidaga ko’ra SQL marshrutda emas, modelda turadi. models/user.js ga qo’shing:

function setAvatar(id, fileName) {
  database
    .prepare("UPDATE users SET avatar_file = ? WHERE id = ?")
    .run(fileName, id);
}

module.exports = { findByEmail, findById, create, verifyPassword, setAvatar, toUser };

Marshrut himoyalangan bo’lishi kerak — aks holda kirmagan odam ham server diskiga fayl yozadi:

const { requireAuth } = require("../middleware/auth");

router.post(
  "/avatar",
  requireAuth,
  upload.single("avatar"),
  (request, response) => {
    if (!request.file) {
      return response.status(400).json({ error: "Fayl yuborilmadi" });
    }

    User.setAvatar(request.session.userId, request.file.filename);
    response.status(201).json({ url: `/uploads/${request.file.filename}` });
  }
);

Fayllarni qayerda saqlash

Diskka yozish o’rganish uchun qulay, lekin cheklovlari bor.

Yondashuv Qachon Cheklov
Server diski Kichik loyiha, bitta server Server almashsa fayllar yo’qoladi
Tashqi ombor (S3 kabi) Ko’p fayl, bir nechta server Qo’shimcha sozlash

Ko’p xosting xizmatida server fayl tizimi vaqtinchalik: ilova qayta joylashtirilsa, uploads/ papkasi tozalanadi. Bunday muhitda foydalanuvchi fayllari faqat tashqi omborga yoziladi. Bu jihat deploy va operatsiyalar darsida tushuntirilgan.

Amaliy mashq

task-app loyihasida davom eting:

  1. multer ni o’rnatib, enctype="multipart/form-data" bilan forma yarating;
  2. diskStorage ni sozlab, fayl nomini crypto.randomUUID() bilan bering va yuklangan fayl nomini uploads/ papkasida ko’ring;
  3. enctype atributini olib tashlab formani yuboring — request.file bo’sh chiqishini tasdiqlang, so’ng qaytaring;
  4. fileFilter yozib, .txt fayl yuborib ko’ring va 400 javobini oling;
  5. limits.fileSize ni 2 MB qilib, kattaroq fayl bilan 413 javobini oling;
  6. express.static bilan /uploads ni ochib, yuklangan rasmni brauzerda ko’ring.

3-qadam eng ko’p vaqt yo’qotadigan xatoni oldindan ko’rsatadi: enctype siz fayl umuman yetib kelmaydi.

Qisqacha xulosa

  • Fayl multipart/form-data formatida yuboriladi; formada enctype yozilishi shart.
  • multer bu formatni o’qiydi va request.file obyektini to’ldiradi.
  • Foydalanuvchi bergan fayl nomi ishlatilmaydicrypto.randomUUID() bilan yangi nom beriladi.
  • MIME turi va kengaytma ikkalasi ham tekshiriladi, lekin ular faylning mazmunini kafolatlamaydi.
  • limits.fileSize har doim qo’yiladi — usiz disk to’ldirilishi mumkin.
  • multer xatolari MulterError turida keladi; hajm chegarasiga 413 javobi mos.
  • Yuklash marshruti himoyalanadi, fayl nomi bazaga yoziladi, eski fayl o’chiriladi.

Keyingi darsda web xavfsizligi — XSS, CSRF, helmet va so’rov chegarasi.

Bu dars foydali bo'ldimi?

marta ko'rildi