React+Node+Axios响应异常:无法捕获文件上传错误
问题描述
我用React.js前端和Node.js后端开发时,遇到响应消息无法正常返回展示的问题。此前该功能正常,但修改文件上传中间件与Route.post逻辑以捕获已上传文件的情况后,现在无法捕获任何错误。相关代码如下:
React 前端代码
import React, { useState, useEffect } from 'react'; import './NewsAndFlyerUploadFlyers.css'; //Components import Header from '../../Components/Header/Header'; import Footer from '../../Components/Footer/Footer'; //Repositories import Axios from 'axios'; import { useNavigate } from 'react-router-dom'; import moment from 'react-moment'; const NewsAndFlyerUploadFlyers = () => { const [flyerName, setFullName] = useState(null); const [flyerEventDate, setFlyerEventDate] = useState(null); const [flyerDescription, setFlyerDescription] = useState(null); const [selectedFile, setSelectedFile] = useState(null); const [statusMessage, setStatusMessage] = useState(null); 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); for (var x = 0; x < selectedFile.length; x++){ data.append('files', selectedFile[x]); } Axios.post(url, data) .then((response) => { if (response.data.message != null){ setStatusMessage(response.data.message); } else { Navigate('/NewsAndFlyers'); } }); setIsLoading(false); }; return ( <> <Header/> <div className='newsAndFlyersUploadFlyersBody'> <h1>Upload A Flyers</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' type='date' 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 onChange={(e) => setSelectedFile(e.target.files)} /></div> {isLoading && <button className='uploadButton' disabled>Loading</button>} {!isLoading && <button className='uploadButton' onClick={submitForm}>Upload Flyer</button>} <h2>{statusMessage}</h2> </div> <Footer/> </> ); } export default NewsAndFlyerUploadFlyers;
Node.js 文件上传中间件
const flyerDestination = './routes/Flyers' const flyerStorage = multer.diskStorage({ destination: function (req, file, cb){ return cb(null, flyerDestination) }, filename: function (req, file, cb){ if (fs.existsSync(path.join(flyerDestination,file.originalname))) { return cb(new Error("Flyer already exists")); } else { return cb(null, file.originalname); } } }); const flyerUpload = multer({ storage: flyerStorage }).array('files');
Node.js 路由代码
router.post('/Flyer_Upload', (req, res) => { flyerUpload(req, res, function (err) { const flyerName = req.body.flyerName; const flyerEventDate = req.body.flyerEventDate; const flyerDescription = req.body.flyerDescription; const flyerID = Math.floor(Math.random()*90000) + 10000; const flyerDirectory = 'http://localhost:3001/Flyers/'; var uploadFlyer = true; var multerMessage = null; if (err instanceof multer.MulterError) { uploadFlyer = false; multerMessage = 'An Error Occurred During Upload!'; } else if (res.status(409)) { uploadFlyer = false; multerMessage = 'Flyer already exists'; } else if (res.status(500)){ uploadFlyer = false; multerMessage = 'A Bad Request Was Recieved!'; } else if (err){ uploadFlyer = false; multerMessage = 'An Unknown Error Occurred!'; } if (multerMessage == null){ for (const file of req.files) { directory = path.dirname(file.path); const flyerUploadedPath = flyerDirectory + file.filename //Delete Once Successful console.log('flyerName: ' + flyerName); console.log('flyerEventDate: ' + flyerEventDate); console.log('flyerDescription: ' + flyerDescription); console.log('flyerID: ' + flyerID); console.log('File path: ' + flyerUploadedPath); db.query('INSERT INTO flyerinfo (flyerID, flyerName, flyerEventDate, flyerDescription, flyerURL) VALUES (?,?,?,?,?)', [flyerID, flyerName, flyerEventDate, flyerDescription, flyerUploadedPath], (err, res) => { if (err){ return res.json({message: 'An Error Occurred!' }); } }); return res.status(200).send(req.files); } } else { console.log (multerMessage) return res.json({message: multerMessage }); } }); }); module.exports = router;
问题分析与修复方案
核心问题点
- 前端未处理请求错误:Axios仅监听成功回调,未捕获网络错误或后端错误响应;
setIsLoading(false)直接写在请求外,会导致请求未完成就关闭加载状态。 - 后端错误判断逻辑错误:
res.status(409)是设置响应状态码的方法,不是错误判断条件;文件名重复的错误会被归类为"未知错误",无法返回对应提示。 - 变量作用域冲突:数据库查询回调中使用
res参数,覆盖了外层的响应对象,导致数据库错误时无法正常返回响应。 - 文件处理逻辑缺陷:循环处理文件时,第一个文件就执行
return res,导致多文件仅处理第一个;且数据库插入是异步操作,提前return会导致插入未完成就返回响应。 - 状态变量命名混乱:
setFullName对应flyerName状态,命名不一致易引发bug。
修复后的前端代码(关键调整)
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); for (var x = 0; x < selectedFile.length; x++){ data.append('files', selectedFile[x]); } Axios.post(url, data) .then((response) => { if (response.data.message != null){ setStatusMessage(response.data.message); } else { Navigate('/NewsAndFlyers'); } }) .catch((error) => { // 捕获所有请求错误并展示 setStatusMessage(error.response?.data?.message || '上传失败,请重试'); }) .finally(() => { // 请求完成后统一关闭加载状态 setIsLoading(false); }); }; // 同时修正状态变量命名 const [flyerName, setFlyerName] = useState(null); // 对应input的onChange改为setFlyerName <input name='flyerName' placeholder='Event Name' required autoComplete="off" onChange={(e) => setFlyerName(e.target.value)} />
修复后的后端路由代码
router.post('/Flyer_Upload', (req, res) => { flyerUpload(req, res, function (err) { const flyerName = req.body.flyerName; const flyerEventDate = req.body.flyerEventDate; const flyerDescription = req.body.flyerDescription; const flyerID = Math.floor(Math.random()*90000) + 10000; const flyerDirectory = 'http://localhost:3001/Flyers/'; var multerMessage = null; // 修正错误判断逻辑 if (err instanceof multer.MulterError) { multerMessage = '上传过程中发生错误!'; } else if (err && err.message === "Flyer already exists") { multerMessage = '该文件已存在!'; res.status(409); } else if (err){ multerMessage = '发生未知错误!'; res.status(500); } if (multerMessage) { console.log(multerMessage); return res.json({message: multerMessage }); } // 用Promise处理异步数据库操作,确保所有文件插入完成后再返回 const insertPromises = req.files.map(file => { const flyerUploadedPath = flyerDirectory + file.filename; return new Promise((resolve, reject) => { db.query( 'INSERT INTO flyerinfo (flyerID, flyerName, flyerEventDate, flyerDescription, flyerURL) VALUES (?,?,?,?,?)', [flyerID, flyerName, flyerEventDate, flyerDescription, flyerUploadedPath], (err, result) => { if (err){ reject('插入数据库失败!'); } else { resolve(result); } } ); }); }); Promise.all(insertPromises) .then(() => { res.status(200).json({message: null}); }) .catch(dbErr => { res.status(500).json({message: dbErr}); }); }); }); module.exports = router;
内容的提问来源于stack exchange,提问作者JonD
相关产品推荐
相关产品推荐

