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目录,原因如下:
src是源码目录:上传的图片属于用户生成内容,不属于项目源码,会被Git忽略(需在.gitignore中排除),编译时也不会处理该目录的非源码文件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
相关产品推荐
相关产品推荐

