Node.js中multer文件大小验证问题:自定义中间件无法获取req.file
问题:Multer中间件前自定义文件大小验证时req.file为undefined
在Node.js应用中使用multer处理文件上传时,尝试通过自定义中间件setFileLimit根据文件类型设置动态大小限制,但该中间件执行时req.file和req.files均为undefined,无法提前验证文件大小。移除setFileLimit后,multer能正常获取文件并完成后续存储及数据库写入操作。
前端代码
// 发送媒体文件的前端逻辑 if (file) { const formData = new FormData() formData.append('medias', file) console.log('send rrrr--'); console.log('type of formdata--', typeof formData); const mediatoCloudinary = await uploadMedia(formData) if (mediatoCloudinary?.status === 400) { console.log('media upload error', mediatoCloudinary); setMediaError('No media file found.') } } // 封装的上传请求函数 export const uploadMedia = async(formData) => { try { console.log('rrr--', formData); const result = await axiosApi.post(messagesRoute.sendMedia, formData) console.log('media link', result); return result.data } catch (error) { console.log('media error', error.message); return error.response } }
后端路由
router.post('/sendMedia', setFileLimit, multerMid.single('medias'), catchAsyncErrors((req: Req, res: Res, next: Next) => { console.log('in send media route'); messageController.saveMediaToCloudinary(req, res, next) }))
后端Multer配置与setFileLimit中间件
// 自定义大小限制中间件 const setFileLimit = (req: Req, res: Res, next: Next) => { console.log('Accessing file:', req.file, 'req.files--', req.files); const file = req.file if (!file) { return res.status(400).send('No media file found.'); } let maxSize; if (file.mimetype.startsWith('image/')) { maxSize = 5 * 1024 * 1024; // 图片最大5MB } else if (file.mimetype.startsWith('video/')) { maxSize = 10 * 1024 * 1024; // 视频最大10MB } else { maxSize = 5 * 1024 * 1024; // 其他文件默认5MB } if (file.size > maxSize) { return res.status(400).json({ error: 'File size exceeds the limit.' }); } next() } // Multer磁盘存储配置 const storage = multer.diskStorage({ destination: (req, file, cb) => { const uploadDirectory = path.join(__dirname, '../public/media') if (!fs.existsSync(uploadDirectory)) { fs.mkdirSync(uploadDirectory, { recursive: true }); } cb(null, uploadDirectory); }, filename: (req, file, cb) => { console.log('file in multer--', file); const uniqueSuffix = uuidv4() + '-' + file.originalname cb(null, uniqueSuffix); }, }); // Multer实例 const multerMid = multer({ storage: storage, limits: { fieldNameSize: 100, fieldSize: 5 * 1024 * 1024 } }); export { multerMid, setFileLimit }
解决方案
核心原因
req.file是multer中间件处理multipart/form-data请求后,才挂载到req对象上的属性。你把setFileLimit放在multer前面执行,此时multer还没解析请求中的文件数据,自然拿不到req.file。
修改方案:将验证逻辑移入multer的fileFilter
multer提供了fileFilter钩子,会在文件开始处理前触发,此时可以拿到文件的mimetype和size,刚好适合做动态大小验证,且不会存储不符合要求的文件。
修改后的multer配置如下:
const multerMid = multer({ storage: storage, // 先设置一个全局最大上限(这里设为视频的最大限制10MB) limits: { fieldNameSize: 100, fileSize: 10 * 1024 * 1024 }, fileFilter: (req, file, cb) => { let maxSize; // 根据文件类型设置对应大小限制 if (file.mimetype.startsWith('image/')) { maxSize = 5 * 1024 * 1024; // 图片5MB } else if (file.mimetype.startsWith('video/')) { maxSize = 10 * 1024 * 1024; // 视频10MB } else { maxSize = 5 * 1024 * 1024; // 其他文件5MB } // 验证文件大小 if (file.size > maxSize) { return cb(new Error(`File size exceeds limit: ${maxSize / 1024 / 1024}MB`), false); } // 验证通过,允许存储文件 cb(null, true); } });
调整路由
移除多余的setFileLimit中间件,路由简化为:
router.post('/sendMedia', multerMid.single('medias'), catchAsyncErrors((req: Req, res: Res, next: Next) => { console.log('in send media route'); messageController.saveMediaToCloudinary(req, res, next) }))
错误处理
multer抛出的错误需要被捕获,你可以在全局错误处理中间件中处理,或者在catchAsyncErrors中捕获并返回对应响应:
// 示例全局错误处理中间件 app.use((err: Error, req: Req, res: Res, next: Next) => { if (err.message.includes('File size exceeds limit')) { return res.status(400).json({ error: err.message }); } // 其他错误处理逻辑 res.status(500).json({ error: 'Server error' }); });
内容的提问来源于stack exchange,提问作者Aswin K S
相关产品推荐
相关产品推荐

