React+Node.js使用Multer上传文件时req.file返回Undefined求助
Multer处理文件上传时req.file返回undefined的问题修复
问题根源
- 前端请求错误:Axios POST请求中,地址、MLS ID等字段被放在了config参数里,而非FormData中,导致后端无法读取这些字段;同时
selectedFile初始值设为字符串,不符合文件对象类型,可能引发遍历错误。 - 后端处理错误:使用
multer.array('file')处理多文件上传时,对应获取文件的属性是req.files而非req.file;路由中先尝试读取req.body,但此时Multer还未解析请求,req.body为空;另外路由文件中使用app.use添加中间件无效,应改用router.use。
修复后的代码
React 代码修改
import React, { useState } from 'react'; import './Properties_Upload.css'; import Navbar from '../../../Components/Navbar/Navbar'; import Footer from '../../../Components/Footer/Footer'; import Axios from 'axios'; import { useNavigate } from 'react-router-dom'; export default function Properties_Upload() { const [addressLine, setAddressLine] = useState(''); const [addressLine2, setAddressLine2] = useState(''); const [addressCity, setAddressCity] = useState(''); const [addressState, setAddressState] = useState(''); const [addressZipCode, setAddressZipCode] = useState(''); const [mlsID, setMLSID] = useState(''); // 修正初始值为null,匹配文件对象类型 const [selectedFile, setSelectedFile] = useState(null); const [errorMessage, setErrorMessage] = useState(''); const navigate = useNavigate(); const propertyInfoHandler = async () => { const data = new FormData() // 将所有表单字段添加到FormData data.append('addressLine', addressLine); data.append('addressLine2', addressLine2); data.append('addressCity', addressCity); data.append('addressState', addressState); data.append('addressZipCode', addressZipCode); data.append('mlsID', mlsID); // 遍历添加选中的文件 if (selectedFile) { for (let x = 0; x < selectedFile.length; x++) { data.append('file', selectedFile[x]); } } Axios.post('http://localhost:3001/property/properties_upload', data) .then((response) => { navigate('/properties'); if (response.data.errorMessage){ setErrorMessage(response.data.message); } }) .catch(err => { setErrorMessage('上传失败,请重试'); console.error(err); }); }; return ( <> <Navbar /> <div className='propertiesUploadBody'> <h1>Properties Upload</h1> <div className='propertyInfoFormBody'> <h1>Property Information</h1> <p className='addressLine'> *Property address: <input name='addressLine' placeholder='Address' required autoComplete="off" onChange={(e) => setAddressLine(e.target.value)} /> </p> <p className='addressLine2'> Apartment/Suite/Unit: <input name='addressLine2' placeholder='Apartment/Suite/Unit' autoComplete="off" onChange={(e) => setAddressLine2(e.target.value)} /> </p> <p className='addressCity'> *Property city: <input name='addressCity' placeholder='City' required autoComplete="off" onChange={(e) => setAddressCity(e.target.value)} /> </p> <p className='addressState'> *Property state: <input name='addressState' placeholder='State' maxLength={2} required autoComplete="off" onChange={(e) => setAddressState(e.target.value)} /> </p> <p className='addressZipCode'> *Property zip code: <input type='number' name='addressZipCode' placeholder='Zip Code' required autoComplete="off" onChange={(e) => setAddressZipCode(e.target.value)} /> </p> <p className='propertyMLSID'> MLS ID: <input type='number' name='propertyMLSID' placeholder='MLS ID' required autoComplete="off" onChange={(e) => setMLSID(e.target.value)} /></p> </div> <div className='propertyFileFormBody'> <h1>Property Image Upload</h1> <p> Upload Images of the property.</p> <input className='uploadFiles' type='file' name='file' multiple required onChange={(e) => setSelectedFile(e.target.files)} /> </div> <h2 style={{color: "red"}}>{errorMessage}</h2> <button className='uploadButton' onClick={propertyInfoHandler}>Upload Property</button> </div> <Footer /> </> ) }
Node.js 代码修改
const express = require('express'); const router = express.Router(); const db = require('../config/db'); const bcrypt = require('bcrypt'); const saltRounds = 10; const session = require('express-session'); const bodyParser = require('body-parser'); const SqlDbStore = require('express-mysql-session')(session); const passport = require('passport'); const cookieParser = require('cookie-parser'); const multer = require('multer'); // 改用router.use添加中间件,确保路由层级生效 router.use(cookieParser()); router.use(bodyParser.json()); router.use(bodyParser.urlencoded({ extended: true })); router.use(passport.session()); router.use(session({ key: 'session_cookie_name', secret: 'session_cookie_secret', store: new SqlDbStore({ host: 'localhost', port: 3306, user: 'root', password: '**************', database: '**************', }), resave: false, saveUninitialized: false, cookie:{ maxAge:1000*60*60*24, secure: false } })); const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, './Images'); }, filename: function (req, file, cb) { cb(null, Date.now() + '-' + file.originalname ); } }); const upload = multer({ storage: storage }).array('file'); // 将upload作为中间件前置,确保先解析文件和表单数据 router.post('/properties_upload', upload, (req, res) => { // 此时可正确读取req.body和req.files const addressLine = req.body.addressLine; const addressLine2 = req.body.addressLine2; const addressCity = req.body.addressCity; const addressState = req.body.addressState; const addressZipCode = req.body.addressZipCode; const mlsID = req.body.mlsID; const rentalID = Math.floor(Math.random()*90000) + 10000; // 多文件上传用req.files(数组)而非req.file console.log("fileInfo: ", req.files); // 此处可添加数据库插入逻辑 // db.query(...) return res.status(200).send({ files: req.files, propertyData: req.body }); }); module.exports = router;
关键说明
- 前端必须把所有需要传递的字段(文件+普通文本)都放入
FormData,Axios会自动设置正确的Content-Type: multipart/form-data。 - 后端使用
multer.array('file')时,文件列表存储在req.files(数组);单个文件上传用multer.single('file')才会使用req.file。 - 路由中需将
upload作为中间件放在处理函数前,确保Multer先完成请求解析,之后才能读取req.body和req.files。
内容的提问来源于stack exchange,提问作者JonD
相关产品推荐
相关产品推荐

