Search code examples
node.jsmongodbmulter

Node.js, Multer. video file is saved/uploaded but validator gives an error that video field can not remain empty


I'm trying to upload a video using multer, I have a form and everything but when I click create, validator gives an error however, the file is saved in my directory as if validation was successful. I'm using mongoDB for database.

here's the validator

const { check } = require('express-validator/check');
const Video = require('app/models/VideoModel');
const path = require('path');

class videoValidator extends validator {
    
    handle() {
        return [
            check('title')
                .isLength({ min : 5 })
                .withMessage('title should be at least 5 characters'),

            check('artist')
                .not().isEmpty()
                .withMessage('artist field can not be empty'),

            check('videos')
                .custom(async (value , { req }) => {
                    if(req.query._method === 'put' && value === undefined) return;

                    if(! value)
                        throw new Error('the video field can not remain empty');

                    let fileExt = ['.webm' , '.ogg' , '.mp4' , '.avi'];
                    if(! fileExt.includes(path.extname(value)))
                        throw new Error('file extention is not supported')
                }),

    }

    
    slug(title) {
        return title.replace(/([^۰-۹آ-یa-z0-9]|-)+/g , "-")
    }
}

module.exports = new videoValidator();

video Controller

async create(req , res) {
        let artists = await Artist.find({});
        res.render('admin/videos/create' , { artists });        
    }

    async store(req , res , next) {
        try {
            let status = await this.validationData(req);
            if(! status) return this.back(req,res);
        
            let videos = req.file;
            let { title , artist } = req.body;

            let newVideo = new Video({
                artist,
                title,
                slug : this.slug(title),
                videos,
            });

            await newVideo.save();

            return res.redirect('/admin/videos');  
        } catch(err) {
            next(err);
        }
    }

Routes and upload helper

const videoUpload = require('app/helpers/uploadVideo');

// video Routes
router.get('/videos' , videoController.index);
router.get('/videos/create' , videoController.create);
router.post('/videos/create' , 
    videoUpload.single('videos'), 
    videoValidator.handle(),
    videoController.store
);

const multer = require('multer');
const path = require('path');
const mkdirp = require('mkdirp');
const fs = require('fs');




const getDirVideo = () => {
    let year = new Date().getFullYear();
    let month = new Date().getMonth() + 1;
    let day = new Date().getDay();

    return `./public/uploads/videos/${year}/${month}/${day}`;
}

const videoStorage =  multer.diskStorage({
    destination : (req , file , cb) => {
        let dir = getDirVideo();

        mkdirp(dir , (err) => cb(null , dir))
    },
    filename : (req , file , cb) => {
        let filePath = getDirVideo() + '/' + file.originalname;
        if(!fs.existsSync(filePath))
            cb(null , file.originalname);
        else
            cb(null , Date.now() + '-' + file.originalname);
            
    }
})

const uploadVideo = multer({
    storage : videoStorage,
    limits : {
        fileSize : 1024 * 1024 * 40
    }
});

module.exports = uploadVideo;

Model and ejs

const videoSchema = Schema({
    artist: {
        type : Schema.Types.ObjectId,
        ref : 'Artist'
    },
    title: {
        type : String,
        required : true
    },
    videos: {
        type : Object,
        required : true
    },
    time: {
        type: String,
        default : '00:00:00'
    },
    videoUrl: {
        type : String,
        required : true
    },
} , { timestamps : true });



module.exports = mongoose.model('Video' , videoSchema);

<div class="d-flex justify-content-between align-items-center mb-3 pb-2 border-bottom">
    <h2>add Video</h2>
</div>
<form class="form-horizontal" action="/admin/videos/create" method="post" enctype="multipart/form-data">

    <%- include(viewPath('layouts/error-messages')) -%>

    <div class="form-group row">
        <div class="col">
            <label for="title" class="control-label font-weight-bold">Video Title             </label> 
            <input type="text" class="form-control" name="title" id="title" placeholder="video title" value="<%= old('title') %>">
        </div>
    </div>
    <div class="form-group row">
        <div class="col">
            <label for="artist" class="control-label font-weight-bold font-weight-bold">linked Artist</label>
            <select name="artist" id="artist" class="form-control">
                <% artists.forEach(artist => { %> 
                    <option value="<%= artist._id %>" <%= String(old('artist')) == String(artist._id) ? 'selected' : '' %> ><%= artist.name %></option>
                <% }) %>
            </select>
        </div>
        <div class="col">
            <label for="time" class="control-label font-weight-bold">Video Length</label>
            <input type="text" class="form-control" name="time" id="time" placeholder="enter video length" value="<%= old('time') %>">
        </div>
    </div>
    <div class="form-group row">
        <div class="col">
            <label for="videos" class="control-label font-weight-bold">Video File</label>
            <input type="file" class="form-control" name="videos" id="videos" placeholder="Enter video file" >
        </div>
    </div>
   
    <div class="form-group row">
        <div class="col">
            <button type="submit" class="btn btn-danger">Create</button>
        </div>
    </div>
</form>

and here's a screen shot of my upload folder that has the video correctly saved in it and it's playable

enter image description here


Solution

  • I just realized I've forgotten to add convertFileToField middleware. it's working fine now

    handle(req , res , next) {
            if(! req.file) 
                req.body.videos = undefined;
            else
                req.body.videos = req.file.filename;
    
            next();
        }