【问题标题】:updating and validating multiple files from different fields uploded with multer使用 multer 更新和验证来自不同字段的多个文件
【发布时间】:2021-02-11 15:21:15
【问题描述】:

我正在尝试更新和验证由两个文件输入(一个视频和一个用作视频缩略图的图像)和两个文本输入组成的表单。 当我上传单个文件时,验证器和 convertFiletoField 工作正常,但是它们都停止工作了,这是一个很长的问题,但如果有人能提供帮助,我将不胜感激。

我正在使用 Node、epxress、mongodb 和 express-validator v.5.1.2

路线:

router.get('/mvs', mvController.index);
router.get('/mvs/create', mvController.create);
router.post('/mvs/create',
    uploadVideo.fields([{
            name: 'videos', maxCount: 1
        }, {
            name: 'images', maxCount: 1
        }]),
    convertFileToField.videoHandle,
    mvValidator.handle(),
    mvController.store
);
router.get('/mvs/:id/edit', mvController.edit);
router.put('/mvs/:id',
    uploadVideo.fields([{
        name: 'videos', maxCount: 1
    }, {
        name: 'images', maxCount: 1
    }]),
    convertFileToField.videoHandle,
    mvValidator.handle(),    
    mvController.update
);
router.delete('/mvs/:id', mvController.destroy);

控制器:

const fs = require('fs');
const path = require('path');
const controller = require('app/http/controllers/controller');
const Artist = require('app/models/artist');
const MV = require('app/models/mv');

class mvController extends controller {
    async index(req , res) {
        try {
            let page = req.query.page || 1;
            let mvs = await MV.paginate({} , { page , sort : { createdAt : 1 } , limit : 5 });
            res.render('admin/mvs/index',  { title : 'videos' , mvs });
        } catch (err) {
            next(err);
        }
    }

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

    async store(req , res , next) {
        try {
            let status = await this.validationData(req);
            if(! status) {
                if(req.file) 
                    fs.unlinkSync(req.file.path);
                return this.back(req,res);
            }
        
            // Create music video
            let videos = this.videoPath(req.files['videos'][0]);
            let images = this.videoPath(req.files['images'][0]);
            let { title, artist} = req.body;
            
            let newMV = new MV({
                title,
                slug : this.slug(title),
                artist,
                videos,
                images
            });
            await newMV.save();

            // update artist Times
            this.updateArtistTime(req.body.artist);

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

    async edit(req, res ,next) {
        try {
            this.isMongoId(req.params.id);

            let mv = await MV.findById(req.params.id);
            let artists = await Artist.find({});
            if( ! mv ) this.error('video does not exist' , 404);
            return res.render('admin/mvs/edit' , { mv , artists });
        } catch (err) {
            next(err);
        }
    }

    async update(req, res , next) {
        try {
            let status = await this.validationData(req);
            if(! status) {
                if(req.files) 
                    fs.unlinkSync(req.files.path);
                return this.back(req,res);
            }

            let objForUpdate = {};

            // check video 
            if(req.files) {
                objForUpdate.videos = this.videoPath(req.files['videos'][0]);
                objForUpdate.images = this.videoPath(req.files['images'][0]);
            }

            delete req.body.videos;
            delete req.body.images;
            
            objForUpdate.slug = this.slug(req.body.title);
            
            let mv = await MV.findByIdAndUpdate(req.params.id , { $set : { ...req.body, ...objForUpdate }});

            // prev artist time update
            this.updateArtistTime(mv.artist);
            // now artist time update
            this.updateArtistTime(req.body.artist);

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

    }

    async destroy(req , res , next) {
        try {
            this.isMongoId(req.params.id);

            let mv = await MV.findById(req.params.id);
            if( ! mv ) this.error('video does not exist' , 404);


            let artistId = mv.artist;

            // delete music videos
            fs.unlinkSync(`./public${mv.videos}`);
            fs.unlinkSync(`./public${mv.images}`)
            mv.remove();

            // artist time update
            this.updateArtistTime(artistId);

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

    async updateArtistTime(artistId) {
        let artist = await Artist.findById(artistId).populate('mvs').exec();
        artist.set({ time : this.getTime(artist.mvs)});
        await artist.save();
    }

    videoPath(video) {
        
        let addressVideos = this.getUrlVideo(`${video.destination}/${video.filename}`);

        return addressVideos;
    }

    getUrlVideo(dir) {
        return dir.substring(8);
    }

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

module.exports = new mvController();

uploadVideo 助手

const multer = require('multer');
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/mvs/${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;
        console.log(filePath);
        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;

converFiletoField

videoHandle(req , res , next) {
        if(! req.files) {
            req.body.videos = undefined;
            req.body.images = undefined;
        }
        else {
            req.body.videos = req.files.videos[0].filename;
            req.body.images = req.files.images[0].filename;
        }
            

        next();
    }

验证器:

const validator = require('./validator');
const { check } = require('express-validator/check');
const Artist = require('app/models/artist');
const MV = require('app/models/mv');
const path = require('path');

class mvValidator extends validator {
    
    handle() {
        return [
            check('title')
                .isLength({ min : 3 })
                .withMessage('title can not be less than 3 characters'),
                

            check('artist')
                .not().isEmpty()
                .withMessage('related artist can not remain empty'),

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

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

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

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

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

                    let fileExt = ['.png' , '.jpg' , '.jpeg' , '.svg'];
                    if(! fileExt.includes(path.extname(value)))
                        throw new Error('file extention is not acceptable')
                })
        ]
    }

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

module.exports = new mvValidator();

我在尝试更新/编辑时遇到的错误

TypeError: Cannot read property '0' of undefined
    at convertFileToField.videoHandle (test\app\http\middleware\convertFileToField.js:22:47)
    at Layer.handle [as handle_request] (test\node_modules\express\lib\router\layer.js:95:5)
    at next (E:\test\node_modules\express\lib\router\route.js:137:13)
    at Immediate._onImmediate (test\node_modules\multer\lib\make-middleware.js:53:37)
    at processImmediate (internal/timers.js:463:21

当我输入带有 2 个字符而不是 3 个字符的标题时,我从验证器收到的错误

TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string or an instance of Buffer or URL. Received undefined

【问题讨论】:

  • 您上次的编辑似乎不小心从您的问题中删除了大部分信息。它需要提供尽可能多的内容,以便尽可能容易和清楚地回答。我为你解开了那个编辑。请再次尝试预期的“较小”编辑。

标签: node.js mongodb express upload multer


【解决方案1】:

尝试创建一个像这样的中间件:

videoHandle.js

module.exports = (req, res, next) => {
  try {
    if (!req.files) {
      req.body.videos = undefined;
      req.body.images = undefined;
    } else {
      req.body.videos = (req.files.videos) ? req.files.videos[0].filename : undefined;
      req.body.images = ( req.files.images)? req.files.images[0].filename : undefined;
    }
    next();
  } catch (err) {
    console.log(err)
    const error = new HttpError("uploading failed!", 403);
    return next(error);
  }
};

需要路由器中的中间件

const videoHandle = require('./videoHandle');//path of videoHandler
router.post('/mvs/create',
    uploadVideo.fields([{
            name: 'videos', maxCount: 1
        }, {
            name: 'images', maxCount: 1
        }]),
    videoHandle,(req,res)=>{
   //do somtethings
    console.log(req.body)
  }
);

【讨论】:

  • 谢谢你的回答,但是我又遇到了一个错误,这次来自 mvController TypeError: Cannot read property '0' of undefined at mvController.update (test\app\http\controllers\admin\mvController.js:81:73) 它在这里:if(req.files) { objForUpdate.videos = this.videoPath(req.files['videos'][0]); //line 81 objForUpdate.images = this.videoPath(req.files['images'][0]); }
  • 你能像我的回答一样更改所有中间件吗??根据我的回答,你可以看到 req.body 中的文件路径?
  • 我也应该更改 mvController 吗?你能好心告诉我,因为我对 nodejs 和 js 还很陌生
  • 出错时请求中是否有视频和图片?
  • 更新表单中有一个图像和一个视频以及两个文本字段,错误发生在控制器内部(我已经在我的问题中上传了控制器代码),它发生在第 81 行,即//check video 之后的代码,在 Controller 的更新中间件中。这是我的表单ibb.co/0BYqwgb 的图像的链接,当我单击编辑按钮而不重新上传图像和视频时会发生错误
猜你喜欢
  • 2021-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多