如何Patch包含图片、文本的用户数据 实现form-data更新头像并删除旧文件
头像更新接口修改方案
现存问题
原有代码存在几个明显逻辑错误,无法实现需求:
- 混用
await和回调函数,User.findById加回调后不会返回Promise,await不会生效 findByIdAndUpdate参数格式错误,第一个参数为查询ID,第二个参数才是要更新的字段- 未对旧头像路径、新上传文件做存在性校验,极易触发异常
- 调用
fs.unlink没有加错误捕获,文件删除失败会直接抛错阻断流程 - 更新后返回的是旧用户数据,不是更新后的结果
修正后完整代码
const fs = require('fs/promises'); // 优先使用Promise版本fs,适配async/await语法 const path = require('path'); // 按需引入,用于校验文件路径合法性 router.patch('/:id', upload.single('profilePic'), async (req, res) => { try { // 1. 校验是否上传了新头像文件 if (!req.file) { return res.status(400).send({message: '未上传头像文件', status: 'Failed'}); } // 2. 查询用户原有数据,获取旧头像存储路径 const oldUser = await User.findById(req.params.id).lean().exec(); if (!oldUser) { return res.status(404).send({message: '用户不存在', status: 'Failed'}); } const oldProfilePicPath = oldUser.profile_pic; // 3. 先更新数据库用户头像路径,避免删旧文件失败导致数据异常 const updatedUser = await User.findByIdAndUpdate( req.params.id, { first_name: req.body.first_name, last_name: req.body.last_name, profile_pic: req.file.path }, { new: true, runValidators: true } // new:true返回更新后的数据,runValidators触发字段规则校验 ); // 4. 数据库更新成功后再删除旧头像,删除异常不阻断主流程 if (oldProfilePicPath) { try { await fs.unlink(oldProfilePicPath); console.log('旧头像删除成功:', oldProfilePicPath); } catch (delErr) { console.error('旧头像删除失败:', delErr.message); // 删除失败仅打日志即可,不返回错误给前端,主流程更新已完成 } } // 5. 返回更新成功结果 return res.status(200).send({ user: updatedUser, status: 'Success' }); } catch (e) { // 全局异常捕获 return res.status(500).send({message: e.message, status: 'Failed'}); } });
接口调用说明
提交form-data请求时按以下参数配置即可:
- 请求方法:
PATCH - 请求路径:
/你的路由前缀/{对应用户ID} - form-data字段:
first_name:新的用户名字,无需修改可传原有值last_name:新的用户姓氏,无需修改可传原有值profilePic:选择本地头像文件,字段名必须和upload.single()内的参数完全一致
注意事项
- 如果头像存储用了相对路径,建议调用
fs.unlink时传入绝对路径,避免删除失败 - 可根据业务需求调整逻辑:如果支持只更新姓名不换头像,可加判断仅当
req.file存在时才更新profile_pic字段、删除旧头像 - 旧头像删除失败的日志可单独归档,方便后续定期清理服务器无效文件
内容的提问来源于stack exchange,提问作者nkil
相关产品推荐
相关产品推荐

