Node.js无法读取React.js提交的Post请求数据及文件问题
问题:React前端上传FormData至Node后端无法读取数据及文件
我采用React.js作为前端、Node.js作为后端、MySQL作为数据库,尝试将前端表单数据及文件通过Post请求提交至后端时,后端无法读取任何FormData值,也接收不到文件。以下是前后端代码,未发现明显配置错误,恳请提供排查建议。
React前端代码
import React, { useState } from 'react'; import './NewsAndFlyerUploadFlyers.css'; // Components import Header from '../../Components/Header/Header'; import Footer from '../../Components/Footer/Footer'; // Libraries import Axios from 'axios'; import { useNavigate } from 'react-router-dom'; const NewsAndFlyerUploadFlyers = () => { const [flyerName, setFullName] = useState(''); const [flyerEventDate, setFlyerEventDate] = useState(''); const [flyerDescription, setFlyerDescription] = useState(''); const [selectedFile, setSelectedFile] = useState(null); const [statusMessage, setStatusMessage] = useState(''); const [isLoading, setIsLoading] = useState(false); const navigate = useNavigate(); const submitForm = () => { setIsLoading(true); const url = 'http://localhost:3001/Flyer/Flyer_Upload'; const data = new FormData(); data.append('flyerName', flyerName); data.append('flyerEventDate', flyerEventDate); data.append('flyerDescription', flyerDescription); if (selectedFile) { for (let x = 0; x < selectedFile.length; x++){ data.append('files', selectedFile[x]); } } Axios.post(url, data, { headers: { 'Content-Type': 'multipart/form-data' } }) .then((response) => { if (response.data.status === 500){ setStatusMessage(response.data.message); } else if (response.data.message === 'All fields must be filled out...'){ setStatusMessage(response.data.message); } else { navigate('/NewsAndFlyers'); } }) .catch(err => { setStatusMessage('上传失败,请重试'); console.error(err); }) .finally(() => { setIsLoading(false); }); }; return ( <> <Header/> <div className='newsAndFlyersUploadFlyersBody'> <h1>Upload A Flyer</h1> <div className='flyerInfoFormBody'> <p className='flyerName'>*Event Name: <input name='flyerName' placeholder='Event Name' required autoComplete="off" onChange={(e) => setFullName(e.target.value)} /></p> <p className='flyerEventDate'>*Event Date: <input name='flyerEventDate' placeholder='Event Date' required autoComplete="off" onChange={(e) => setFlyerEventDate(e.target.value)} /></p> <p className='flyerDescription'>*Flyer Description: <textarea name='flyerDescription' placeholder='Flyer Description' required autoComplete="off" onChange={(e) => setFlyerDescription(e.target.value)} /> </p> <p>*Upload the flyer.</p> <input className='uploadFiles' type='file' name='file' required multiple onChange={(e) => setSelectedFile(e.target.files)} /></div> <button className='uploadButton' onClick={submitForm} disabled={isLoading}>{isLoading ? 'Uploading...' : 'Upload Flyer'}</button> <h2>{statusMessage}</h2> </div> <Footer/> </> ); } export default NewsAndFlyerUploadFlyers;
Node.js后端代码(原代码)
const express = require('express'); const router = express.Router(); const db = require('../config/db'); const session = require('express-session'); const bodyParser = require('body-parser'); const app = express(); const SqlDbStore = require('express-mysql-session')(session); const cookieParser = require('cookie-parser'); const multer = require('multer'); const path = require('path'); // 中间件挂载错误:这里新建的app实例不是主应用的,中间件不会生效 app.use(cookieParser()); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.use(session({ key: 'session_cookie_name', secret: 'session_cookie_secret', store: new SqlDbStore({ host: 'localhost', port: 3306, user: 'root', password: 'xxxxxx', database: 'xxxxxx', }), resave: false, saveUninitialized: false, cookie:{ maxAge:1000*60*60*24, secure: false } })); const multerFlyerDestination = './routes/Flyers'; const flyerStorage = multer.diskStorage({ destination: function (req, file, cb){ return cb(null, multerFlyerDestination) }, filename: function (req, file, cb){ // 原filename会覆盖,因为所有文件都叫'files',需要加唯一标识 return cb(null, file.fieldname); } }); // 前端传的是多个'files'字段,应该用array而不是single const uploadFlyer = multer({ storage: flyerStorage }).single('file'); router.post('/Flyer_Upload', (req, res) => { // 这里读取req.body时机错误,multer处理前req.body是空的 const flyerName = req.body.flyerName; const flyerEventDate = req.body.flyerEventDate; const flyerDescription = req.body.flyerDescription; const flyerID = Math.floor(Math.random()*90000) + 10000; console.log('\nflyerName: ' + flyerName); console.log('\nflyerEventDate: ' + flyerEventDate); console.log('\nflyerDescription: ' + flyerDescription); console.log('\nflyerID: ' + flyerID); uploadFlyer(req, res, function (err) { if (err instanceof multer.MulterError) { return res.status(500).json(err) } else if (err) { return res.status(500).json(err) } const directory = path.dirname(req.path); console.log('File path: ' + directory); // single的话是req.file,array的话是req.files return res.status(200).send(req.files); }); }); module.exports = router;
排查及修改建议
1. 修正Multer配置与前端字段不匹配问题
- 前端FormData中文件的key是
files(循环append了多次'files'),后端当前用.single('file'),应该改为.array('files')来接收多个文件; - 原multer的filename会导致文件覆盖,因为所有文件都用
file.fieldname作为文件名,建议改为带时间戳的唯一文件名:return cb(null,${Date.now()}-${file.originalname});; - 后端读取文件时,
.array()对应req.files,.single()对应req.file,要统一。
修改后的multer配置:
const flyerStorage = multer.diskStorage({ destination: function (req, file, cb){ return cb(null, multerFlyerDestination) }, filename: function (req, file, cb){ return cb(null, `${Date.now()}-${file.originalname}`); } }); const uploadFlyer = multer({ storage: flyerStorage }).array('files');
2. 修复后端中间件挂载错误
路由文件中不需要新建app实例,中间件应该在主应用入口文件(比如app.js)中挂载,路由文件仅导出router即可:
主应用入口示例:
const express = require('express'); const app = express(); const session = require('express-session'); const bodyParser = require('body-parser'); const SqlDbStore = require('express-mysql-session')(session); const cookieParser = require('cookie-parser'); const flyerRouter = require('./routes/flyer'); // 导入你的路由 // 挂载中间件 app.use(cookieParser()); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.use(session({ key: 'session_cookie_name', secret: 'session_cookie_secret', store: new SqlDbStore({ host: 'localhost', port: 3306, user: 'root', password: 'xxxxxx', database: 'xxxxxx', }), resave: false, saveUninitialized: false, cookie:{ maxAge:1000*60*60*24, secure: false } })); // 挂载路由 app.use('/Flyer', flyerRouter); app.listen(3001, () => console.log('Server running on port 3001'));
路由文件简化为:
const express = require('express'); const router = express.Router(); const multer = require('multer'); const path = require('path'); const multerFlyerDestination = './routes/Flyers'; const flyerStorage = multer.diskStorage({ destination: function (req, file, cb){ return cb(null, multerFlyerDestination) }, filename: function (req, file, cb){ return cb(null, `${Date.now()}-${file.originalname}`); } }); const uploadFlyer = multer({ storage: flyerStorage }).array('files'); router.post('/Flyer_Upload', (req, res) => { // 移到multer回调内读取req.body uploadFlyer(req, res, function (err) { if (err instanceof multer.MulterError) { return res.status(500).json(err) } else if (err) { return res.status(500).json(err) } const flyerName = req.body.flyerName; const flyerEventDate = req.body.flyerEventDate; const flyerDescription = req.body.flyerDescription; const flyerID = Math.floor(Math.random()*90000) + 10000; console.log('\nflyerName: ' + flyerName); console.log('\nflyerEventDate: ' + flyerEventDate); console.log('\nflyerDescription: ' + flyerDescription); console.log('\nflyerID: ' + flyerID); console.log('\nFiles received:', req.files); const directory = path.dirname(req.path); console.log('File path: ' + directory); return res.status(200).json({message: '上传成功', files: req.files}); }); }); module.exports = router;
3. 调整前端请求及状态处理
- 状态初始值设为空字符串
'',避免null导致FormData值异常; - 给file input加上
multiple属性(如果需要支持多文件上传); - Axios请求手动指定
Content-Type为multipart/form-data,确保正确识别; - 增加错误捕获和finally处理loading状态,避免loading一直为true。
4. 验证FormData构造是否正确
在前端submitForm中添加console.log,查看FormData内容:
// 构造FormData后添加 for (let pair of data.entries()) { console.log(pair[0]+ ', ' + pair[1]); }
内容的提问来源于stack exchange,提问作者JonD
相关产品推荐
相关产品推荐

