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

MERN栈Formik+Multer图片上传报错及存储位置咨询

问题解答

一、FormData提交JSON解析错误的解决办法

你遇到的Unexpected token '<'错误,说白了就是后端返回了HTML页面(比如404错误页、报错页面),但前端还在把它当JSON解析,自然就炸了。常见触发原因有这几个:

  • 前端请求头配错了,后端认不出FormData格式
  • 后端路由没匹配上,返回了404的HTML页面
  • Multer配置出问题,文件上传失败后后端返回了HTML错误
  • 用Formik提交时瞎搞,把FormData转成JSON了(比如JSON.stringify)

具体修复步骤

1. 修正前端请求头

提交FormData的时候绝对不能设Content-Type: application/json,浏览器会自动为FormData设置正确的multipart/form-data请求头(带分隔符)。检查你的http-hook.ts,别手动修改请求头:

// 错误写法:硬塞JSON请求头
axios.post(url, formData, { headers: { 'Content-Type': 'application/json' } });

// 正确写法:让浏览器自动处理请求头
axios.post(url, formData);
// 或者显式设置multipart,但无需指定boundary
axios.post(url, formData, { headers: { 'Content-Type': 'multipart/form-data' } });

2. 调整Formik提交逻辑

Formik默认会把表单数据转成JSON,所以得自定义提交逻辑,直接传递FormData。在SignupForm.tsx里修改onSubmit:

const onSubmit = async (values: FormValues, { setSubmitting }) => {
  const formData = new FormData();
  // 添加图片文件(假设ImageInput返回的是File对象)
  formData.append('avatar', values.avatar);
  // 添加其他表单字段
  formData.append('name', values.name);
  formData.append('email', values.email);

  try {
    // 调用http-hook发送请求,别用JSON.stringify处理FormData!
    await sendRequest('/api/users/signup', 'POST', formData);
    // 提交成功后的逻辑(比如跳转、提示)
  } catch (err) {
    // 错误处理
  } finally {
    setSubmitting(false);
  }
};

3. 检查后端路由与Multer配置

  • 确认user-routes.ts的路由路径和前端请求路径匹配,且app.ts中已正确挂载用户路由
  • 检查file-upload.ts的Multer配置,确保存储路径有效、权限正常:
import multer from 'multer';

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    // 确保uploads/images目录已存在,否则会报错
    cb(null, 'uploads/images'); // 用项目根目录下的uploads,别往src/dist塞
  },
  filename: (req, file, cb) => {
    // 加时间戳避免文件名重复
    cb(null, Date.now() + '-' + file.originalname);
  }
});

const fileFilter = (req, file, cb) => {
  // 只允许图片上传
  if (file.mimetype.startsWith('image/')) {
    cb(null, true);
  } else {
    cb(new Error('仅支持图片文件'), false);
  }
};

export const upload = multer({ storage, fileFilter });
  • 确保users-controller.tsx返回JSON响应,而非HTML:
export const signup = async (req, res, next) => {
  try {
    const { name, email } = req.body;
    const avatar = req.file; // 对应Multer的single('avatar')配置

    // 处理用户创建逻辑...

    res.status(201).json({ message: '用户创建成功', userId: newUser.id });
  } catch (err) {
    // 出错时返回JSON错误,别返回HTML或重定向
    res.status(500).json({ error: err.message });
  }
};

4. 排查后端返回HTML的具体原因

用Postman直接测试后端接口,发送multipart/form-data请求,看返回内容是JSON还是HTML。如果是HTML,大概率是:

  • 路由路径不匹配(比如前端请求/api/signup,后端路由是/users/signup)
  • Multer中间件未正确挂载(比如路由里没加upload.single('avatar'))
  • Express静态文件路由覆盖了API路由

二、图片存储目录选择(TypeScript环境)

绝对不要存在src或dist目录,原因如下:

  1. src是源码目录:上传的图片属于用户生成内容,不属于项目源码,会被Git忽略(需在.gitignore中排除),编译时也不会处理该目录的非源码文件
  2. dist是编译产物目录:每次执行tsc或构建命令时,dist目录会被清空重生成,上传的图片会被覆盖丢失

正确做法

在项目根目录下创建独立的uploads/images目录,并在.gitignore中添加:

uploads/

后端配置直接指向该路径(如上面Multer配置中的'uploads/images')。如果需要前端访问这些图片,在app.ts中配置Express静态文件服务:

import express from 'express';
const app = express();

// 前端可通过http://域名/uploads/xxx.jpg访问图片
app.use('/uploads', express.static('uploads'));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 13:55:16