Node.js Multer问题:使用upload.single()中间件时req.file始终为undefined
问题:Multer自定义上传中间件中req.file始终为undefined
我尝试实现一个可自定义(支持指定文件类型、目标文件夹等)的Multer文件上传中间件,文件上传功能能正常运行,但req.file始终返回undefined。以下是相关代码:
路由代码
router.patch('/profile-picture', [ AsyncErrorWrapper(passport.verifyJwt()), AsyncErrorWrapper(singleFileUpload('/images/', ['image/png', 'image/jpeg'])), ], AsyncErrorWrapper(userController.updateProfilePicture))
singleFileUpload 中间件
const {uploadConfiguration, multerDelay} = require("../services/multer.service") /** * * @param {*} dest 目标文件夹(位于/tmp下) * @param {*} whitelist 允许上传的文件类型列表 * @returns */ module.exports = (dest, whitelist, name = 'file') => { return async (req, res, next) => { // 获取上传配置对象 const upload = uploadConfiguration(dest, whitelist) // 调用upload.single upload.single(name)(req, res, next) // 尝试等待上传完成以解决req.file为undefined的问题 await multerDelay() next() } }
Multer服务代码
const uploadConfiguration = (dest, whitelist) => { // 存储配置 const storage = multer.diskStorage({ destination: (req, file, cb) => { // 设置目录 const dir = `${tmpStorage}${dest}` mkdirp.sync(dir) cb(null, dir) }, filename: (req, file, cb) => { cb(null, Date.now() + path.extname(file.originalname)); }, }) // 上传过滤器 const uploadFilter = (req, file, cb) => { if (!whitelist.includes(file.mimetype)) { return cb(new AppError('文件类型不允许', 400), false) } // 尝试解决后续中间件中req.file为undefined的问题 req.file = file cb(null, true) } return multer({ storage: storage, fileFilter: uploadFilter }) } /** * * @returns 一个在队列清空后resolve的Promise */ const multerDelay = () => { console.log('multerDelay'); return new Promise((resolve) => setTimeout(() => resolve(), 0)) }
控制器代码
const updateProfilePicture = async (req, res, next) => { console.log(req.file) // 输出undefined return res.status(200).json({msg: 'hi'}) }
问题原因分析
- 中间件执行顺序错误:你在
singleFileUpload中手动调用upload.single(name)(req, res, next)后,又通过await multerDelay()和手动next()提前进入下一个中间件。但upload.single是异步处理文件上传的,它会在文件处理完成后才调用传入的next,而你手动调用的next会在Multer还没完成文件处理、赋值req.file时就触发,导致控制器中req.file为undefined。 - 错误手动赋值req.file:在
uploadFilter中手动给req.file = file是多余且错误的,Multer会在文件成功存储后自动将完整的文件信息(包括存储路径、文件名等)赋值给req.file,此处的file只是原始文件对象,并非最终的处理结果。
解决方案
1. 修改singleFileUpload中间件
去掉多余的async包装、multerDelay和手动next(),直接返回Multer的中间件,让它自己处理流程:
const {uploadConfiguration} = require("../services/multer.service") module.exports = (dest, whitelist, name = 'file') => { const upload = uploadConfiguration(dest, whitelist) // 直接返回multer的single中间件,由它负责调用next return upload.single(name) }
2. 修正uploadFilter
移除手动赋值req.file的代码:
const uploadFilter = (req, file, cb) => { if (!whitelist.includes(file.mimetype)) { return cb(new AppError('文件类型不允许', 400), false) } cb(null, true) }
3. 路由保持不变
原路由代码无需修改,AsyncErrorWrapper可以正常包裹修改后的singleFileUpload返回的中间件。
原理说明
Multer的upload.single()本身就是一个符合Express规范的中间件,它会在完成文件的存储、赋值req.file等操作后自动调用next(),不需要额外的异步等待或手动触发next。之前的包装逻辑破坏了Multer的原生执行流程,导致req.file还未被赋值就进入了控制器。
内容的提问来源于stack exchange,提问作者aymen bennour
相关产品推荐
相关产品推荐

