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:
multerni o’rnatib,enctype="multipart/form-data"bilan forma yarating;diskStorageni sozlab, fayl nominicrypto.randomUUID()bilan bering va yuklangan fayl nominiuploads/papkasida ko’ring;enctypeatributini olib tashlab formani yuboring —request.filebo’sh chiqishini tasdiqlang, so’ng qaytaring;fileFilteryozib,.txtfayl yuborib ko’ring va400javobini oling;limits.fileSizeni 2 MB qilib, kattaroq fayl bilan413javobini oling;express.staticbilan/uploadsni 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-dataformatida yuboriladi; formadaenctypeyozilishi shart. multerbu formatni o’qiydi varequest.fileobyektini to’ldiradi.- Foydalanuvchi bergan fayl nomi ishlatilmaydi —
crypto.randomUUID()bilan yangi nom beriladi. - MIME turi va kengaytma ikkalasi ham tekshiriladi, lekin ular faylning mazmunini kafolatlamaydi.
limits.fileSizehar doim qo’yiladi — usiz disk to’ldirilishi mumkin.- multer xatolari
MulterErrorturida keladi; hajm chegarasiga413javobi mos. - Yuklash marshruti himoyalanadi, fayl nomi bazaga yoziladi, eski fayl o’chiriladi.
Keyingi darsda web xavfsizligi — XSS, CSRF, helmet va so’rov chegarasi.