You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

前端更新用户头像失效求助:姓名邮箱可正常更新

问题分析:前端提交头像无法更新,仅姓名邮箱正常

实现用户头像、姓名及邮箱更新功能时,后端测试完全正常,但前端提交请求时,仅姓名和邮箱能成功更新,头像无法同步更新,核心原因及解决方案如下:

前端原有代码

const userDataForm = document.querySelector('.profile-update');

if (userDataForm)
  userDataForm.addEventListener('submit', async (e) => {
    e.preventDefault();
    const name = document.getElementById('nameInput').value;
    const email = document.getElementById('emailInput').value;
    const image = document.getElementById('profile-img-file-input').files[0];

    try {
      const res = await fetch('/api/v1/users/updateMe', {
        method: 'PATCH',
        body: JSON.stringify({ name, email, image }),
        headers: { 'Content-Type': 'application/json' },
      });
      console.log(res);
      const data = await res.json(); 
      if (data.status == 'success') {
        alert('success');
      }
    } catch (error) {
      alert(error);
    }
  });

profile.ejs页面代码

<div class="profile-user position-relative d-inline-block mx-auto mb-4">
  <img
    src="/assets/img/<%= user.photo %>"
    id="user-profile-image"
    class="rounded-circle avatar-xl img-thumbnail user-profile-image"
    alt="user-profile-image"
  />
  <div class="avatar-xs p-0 rounded-circle profile-photo-edit">
    <input
      id="profile-img-file-input"
      name="photo"
      accept="image/*"
      type="file"
      class="profile-img-file-input"
    />
    <label for="profile-img-file-input" class="profile-photo-edit avatar-xs">
      <span class="avatar-title rounded-circle bg-light text-body">
        <i class="ri-camera-fill"></i>
      </span>
    </label>
  </div>
</div>

后端控制器代码

const multerStorage = multer.memoryStorage();

const multerFilter = (req, file, cb) => {
  if (file.mimetype.startsWith('image')) {
    cb(null, true);
  } else {
    cb(new AppError('not an image! please upload only images', 400), false);
  }
};
const upload = multer({
  storage: multerStorage,
  fileFilter: multerFilter,
});

exports.uploadUserPhoto = upload.single('photo');

exports.resizeUserPhoto = catchAsync(async (req, res, next) => {
  if (!req.file) return next();
  req.file.filename = `user-${req.user.id}-${Date.now()}.jpeg`;

  await sharp(req.file.buffer)
    .resize(500, 500)
    .toFormat('jpeg')
    .jpeg({ quality: 90 })
    .toFile(`public/assets/img/${req.file.filename}`);
  next();
});

const filterObj = (obj, ...allowedFields) => {
  const newObj = {};
  Object.keys(obj).forEach((el) => {
    if (allowedFields.includes(el)) newObj[el] = obj[el];
  });

  return newObj;
};


exports.updateMe = async (req, res, next) => {
  if (req.body.password || req.body.passwordConfirm) {
    return next(new AppError('This route is not for password update Please use /updateMyPassword', 400));
  }

  const filteredBody = filterObj(req.body, 'name', 'email');
  if (req.file) filteredBody.photo = req.file.filename;

  const updatedUser = await User.findByIdAndUpdate(req.user._id, filteredBody, {
    new: true,
    runValidators: true,
  });

  res.status(200).json({
    status: 'success',
    data: {
      user: updatedUser,
    },
  });
};

路由配置代码

router.patch('/updateMe', userController.uploadUserPhoto, userController.resizeUserPhoto, userController.updateMe);

核心问题与解决方案

  1. 问题根源:前端尝试用JSON.stringify传输文件对象,这是无效的——JSON无法序列化二进制文件,导致后端multer无法解析到req.file,自然不会更新头像字段。后端配置的upload.single('photo')是用来解析multipart/form-data格式的文件上传,而非JSON格式。

  2. 修改后的前端代码:使用FormData来构造请求体,浏览器会自动设置正确的Content-Type(包含multipart边界):

const userDataForm = document.querySelector('.profile-update');

if (userDataForm)
  userDataForm.addEventListener('submit', async (e) => {
    e.preventDefault();
    const formData = new FormData();
    formData.append('name', document.getElementById('nameInput').value);
    formData.append('email', document.getElementById('emailInput').value);
    const image = document.getElementById('profile-img-file-input').files[0];
    if (image) formData.append('photo', image); // 键名与后端upload.single('photo')保持一致

    try {
      const res = await fetch('/api/v1/users/updateMe', {
        method: 'PATCH',
        body: formData,
        // 无需手动设置Content-Type,浏览器会自动添加正确的multipart/form-data及边界
      });
      console.log(res);
      const data = await res.json(); 
      if (data.status == 'success') {
        alert('success');
        // 可选:实时更新页面头像显示
        document.getElementById('user-profile-image').src = `/assets/img/${data.user.photo}`;
      }
    } catch (error) {
      alert(error);
    }
  });
  1. 额外验证点:
  • 前端input的name属性已设置为photo,与后端upload.single('photo')参数一致,这部分无需修改。
  • 后端保存文件路径为public/assets/img/,前端头像src路径为/assets/img/<%= user.photo %>,路径匹配正常。

内容的提问来源于stack exchange,提问作者Tosin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 15:10:05