NodeJS用户注册接口如何先校验用户名再存储上传图片
问题根因
multipart/form-data 是流式传输协议,默认将multer上传中间件作为路由前置中间件使用时,必须等multer解析完整个请求(含所有文本字段和文件)才会进入业务逻辑,因此无法在文件上传前获取req.body做校验。
可行解决方案
方案1:内存存储+校验后落地文件(最推荐,无接口改造量)
multer解析请求时会按传输顺序处理字段,浏览器提交表单时默认先传所有文本字段、再传文件,解析后的文本字段会提前挂载到req.body。你可以先把文件暂存到内存,校验通过后再写入磁盘:
const multer = require('multer') // 配置为内存存储,文件不会直接写入磁盘 const upload = multer({ storage: multer.memoryStorage() }) app.post('/register', upload.single('profile_pic'), async (req, res) => { const { username, name, password } = req.body // 优先校验用户名 const existUser = await User.findOne({ username }) if (existUser) { return res.status(400).json({ msg: '用户名已被注册' }) } // 校验通过后再将内存中的文件写入磁盘 const picSavePath = `/uploads/${Date.now()}_${req.file.originalname}` require('fs').writeFileSync(`./public${picSavePath}`, req.file.buffer) // 写入用户数据到数据库 await User.create({ name, username, password, profile_pic: picSavePath }) return res.json({ msg: '注册成功' }) })
内存中的临时文件会在请求结束后自动被垃圾回收,不会产生残留垃圾文件。
方案2:fileFilter钩子内置校验逻辑
如果不想用内存存储,也可以直接在multer的fileFilter钩子中完成用户名校验,此时所有先于文件传输的文本字段已经挂载到req.body:
const multer = require('multer') const upload = multer({ storage: multer.diskStorage({ destination: './public/uploads', filename: (req, file, cb) => cb(null, `${Date.now()}_${file.originalname}`) }), fileFilter: async (req, file, cb) => { const existUser = await User.findOne({ username: req.body.username }) if (existUser) { // 校验不通过,直接拦截文件上传 cb(new Error('用户名已被注册'), false) return } cb(null, true) } }) app.post('/register', upload.single('profile_pic'), async (req, res) => { // 到达此处时已经完成校验和文件上传,直接写入数据库即可 await User.create({ ...req.body, profile_pic: `/uploads/${req.file.filename}` }) return res.json({ msg: '注册成功' }) }) // 新增错误处理中间件捕获fileFilter抛出的校验错误 app.use((err, req, res, next) => { if (err.message === '用户名已被注册') { return res.status(400).json({ msg: err.message }) } next(err) })
方案3:拆分校验接口(可选,优化用户体验)
额外新增独立的用户名可用性校验接口,前端在用户输入用户名失焦时就提前调用校验,确认用户名可用后再提交带文件的注册请求,既避免了无效的文件上传,也能提前给用户反馈。
内容的提问来源于stack exchange,提问作者Suraj Singh
相关产品推荐
相关产品推荐

