مزامنة رفع الملفّات ثم تنفيذ أمر آخر بعد الانتهاء في NodeJS

فراس اللومنذ سنتين

المستوى: مُبتدأ/متوسّط

 

يُمكن الاستفادة من مكتبة Multer في nodejs لرفع الصور والملفّات إلى السيرفر، فبأسطر برمجية بسيطة يُمكن رفع الملفات التي قام المُستخدم بتحديدها


let app = require("express")()
let multer = require("multer")
let upload = multer({ dest: 'uploads/' }).any()

app.get("/", (req,res)=>{
    res.send("Teting...")
})


app.post("/upload", (req, res)=>{
    upload(req,res, (err)=>{
        if (err) throw err
    })
    res.send(req.files)
})
app.listen(3000, ()=>{
    console.log("Let's Upload....")
})

الكود السابق يقوم باستلام الملفّات التي يُرسلها المستخدم إلى المسار /upload ليقوم برفعها ثم طباعة اسم الملف المُستلم. لكن وبعد تنفيذه قد تتفاجئ بأن عملية الرفع تجري بنجاح، إلا أن الاسم لا يظهر ويتم إرسال مصفوفة فارغة على الرغم من أن عملية الرفع تمّت دون مشاكل.

السبب في ذلك هو طبيعة Nodejs، والتي سبق وأن تحدّثت عنها في مقال سابق بعنوان "تنفيذ الطلبات بتسلسل وترتيب في Nodejs باستخدام Async/Await". انصحك بقرائته لفهم آلية عمل إطار العمل بشكل كامل.

ما يحدث على أرض الواقع هو أن النظام يبدأ بالرفع، ولأن تنفيذ المهام في nodejs يتم بشكل متوازي لتجنّب تأخير الرد على طلب المستخدم، سيقوم النظام فورًا بطباعة محتويات مصفوفة الملفّات req.files، التي ستكون فارغة بالفعل لأن النظام لم ينتهي بالأصل من عملية الرفع. والحل في هذه الحالة هو استخدام async/await ليُصبح الكود من الشكل:


let app = require("express")()
let multer = require("multer")
let upload = multer({ dest: 'uploads/' }).any()

app.get("/", (req,res)=>{
    res.send("Testing Routes.....")
})

let uploadPromise = (req, res)=>{
    return new Promise((Resolve, Reject)=>{
        upload(req,res, (err)=>{
            if (err) Reject(err)
            Resolve()
        })
    })
}

app.post("/upload", async(req, res)=>{
    try{
        await uploadPromise(req, res)
        res.send(req.files)
    }catch(uploadErr){
        throw uploadErr
    }
    upload(req,res, (err)=>{
        if (err) throw err
    })
    
})
app.listen(3000, ()=>{
    console.log("Let's Upload....")
})

حاول تنفيذ الكود السابق وسترى أن اسم الملف سيظهر دون مشاكل لأن النظام قام أولًا برفع الملف ثم قام بطباعة الاسم، وهذا يعني أن المصفوفة لم تعد فارغة بشكل كامل. الفيديو التالي يشرح الفرق بشكل عملي:

https://www.youtube.com/embed/VNg5uids31k?feature=oembed

أما الراغبين بتحديد لاحقة الملف أو قبول نوع مُحدّد من الملفات فهم بحاجة لتعديل بعض جزئيات الكود، ولتجنّب تعقيد الأفكار ما بين Async/Await، سأقوم باستخدا مثال بسيط عن Multer.


let app = require("express")()
let multer = require("multer")
let upload = multer({dest: "uploads"})

 
app.post("/", upload.single("photos"), (req,res)=>{
    res.send("OK...")

})
app.listen(3000, ()=>{
    console.log("Ready to upload...")
})

الكود السابق يشرح طريقة رفع ملف إلى مُجلّد uploads باستخدام multer، فعند طلب الرابط domain.com/ باستخدام POST فإن النظام سيأخذ الملف الموجود في الحقل photos وسيقوم برفعه. لكن ماذا لو أردنا تغيير الاسم أو اختيار اللاحقة؟ الأمر يتم من خلال ميثود موجودة داخل multer تُعرف بـ diskStorage ليُصبح الكود من الشكل


let app = require("express")()
let multer = require("multer")

let storage = multer.diskStorage({
    destination: function(req, file, cb){
    cb(null, "uploads")
},
    filename: function(req, file, cb){
        let ext = file.mimetype.split("/")
        ext = ext[ext.length-1]
        cb(null, file.originalname + "-"+Date.now()+"."+ext)
    }
})

let upload = multer({storage:storage})

 app.get("/", (req, res)=>{
     console.log(Date.now())
 })

app.post("/", upload.single("photos"), (req,res)=>{
    res.send("OK...")

})
app.listen(3000, ()=>{
    console.log("Ready to upload...")
})

قُمنا في الميثود بتعريف خاصيّتين الأولى هي destination الخاصّة بتحديد مكان الرفع، والثانية هي اسم الملف. الشرط الوحيد في الخصائص داخل multer هو استخدام دالة cb، وهي اختصار لـ Call Back لتمرير النتيجة التي نرغب بها.

في مثالي في الأعلى قُمت بفحص نوع الملف باستخدام الـ mimeType الخاص به، فلو كان صورة سيظهر بالشكل image/jpeg أو image/png وهكذا. بعدها قمت بإضافة تلك اللاحقة للاسم الذي يُمكنك تسميته كيفما تشاء. أما بخصوص رفع نوع واحد من الملفات دونًا عن الغير فهذا مُمكن عبر تعديل السطر الخاص بتعريف upload ليُصبح من الشكل 


let upload = multer({storage:storage,  fileFilter: function(req, file, cb){
    if (file.mimetype != "image/jpeg"){
        return cb(null, false)
    }
    cb(null, true)
}})

قُمنا بإضافة ما يُعرف بالـ fileFilter، وهي خاصيّة موجودة في multer قُمنا فيها بفحص نوع الملف مرّة أُخرى عبر mimeType وفي حالة عدم مُطابقة الشروط نقوم برفض الملف من خلال استخدام إعادة الـ callback فارغ مع false. بهذه الحالة سيقفز النظام عن الملف وسيتجاوزه.

الفيديو التالي يشرح كل شيء

https://www.youtube.com/embed/ZuR1A3rQpW4?feature=oembed

 

في حالة وجود أي استفسار يُمكن تركه في التعليقات أو مُراسلتي عبر حسابي في تويتر @FerasAllaou

كلمات دليلية:
1
إعجاب
815
مشاهدات
0
مشاركة
0
متابع
متميز
محتوى رهيب

التعليقات (0)

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !