React+Express+Axios多图上传至MySQL数据库问题求助
问题排查与修复方案
核心问题总结
- 前后端响应状态码不匹配,导致前端误判为错误
- 后端处理多文件上传时,错误获取文件名(
req.files是数组而非单个对象) - 前端状态管理混乱:同一
files状态同时存储用户选择的文件对象和数据库返回的图片数据 - 图片访问路径错误:静态文件托管在Express服务(3001端口),而非React开发服务(3000端口)
前端代码修复
1. 拆分状态管理
新增selectedFiles状态存储用户选择的上传文件,原files状态仅用于存储数据库返回的图片数据
2. 修正响应判断逻辑
后端返回的状态码需与前端判断匹配,同时上传成功后重新拉取图片列表
3. 修正图片访问路径
将图片src改为Express服务的静态文件路径
修复后的前端代码:
import { useState, useEffect } from 'react'; import axios from 'axios'; export function Media() { // 存储数据库返回的图片数据 const [files, setFiles] = useState([]); // 存储用户选择的待上传文件 const [selectedFiles, setSelectedFiles] = useState([]); useEffect(() => { axios.get("http://localhost:3001/media") .then(res => { console.log(res.data); setFiles(res.data); }) .catch(err => console.log(err)); }, []); const setImgFile = (e) => { setSelectedFiles([...e.target.files]); }; const addUserData = async (e) => { e.preventDefault(); const formData = new FormData(); // 遍历用户选择的文件,而非数据库返回的旧数据 for (const file of selectedFiles) { formData.append('files', file); } // Axios会自动处理multipart/form-data的Content-Type,无需手动设置 try { const res = await axios.post("http://localhost:3001/media", formData); if (res.status === 201) { console.log("上传成功"); // 上传成功后重新拉取最新图片列表 axios.get("http://localhost:3001/media") .then(res => setFiles(res.data)) .catch(err => console.log(err)); // 清空选择的文件 setSelectedFiles([]); } } catch (err) { console.log("上传失败", err); } }; return ( <div style={{ padding: 15, backgroundColor: '#fff', marginTop: 15 }}> <h4>Upload file:</h4> <input type="file" name='photo' onChange={setImgFile} multiple // 清空文件选择框的显示 value={selectedFiles.length === 0 ? '' : undefined} /> <button onClick={addUserData}>submit</button> <ul> {files.map((file, i) => ( <li key={i}> {/* 修正为Express服务的静态文件路径 */} <img src={`http://localhost:3001/images/${file.image}`} alt="上传图片" /> </li> ))} </ul> </div> ); } export default Media;
后端代码修复
1. 引入path模块(处理文件扩展名)
2. 处理多文件上传:循环插入每个文件的文件名到数据库
3. 设置正确的响应状态码,匹配前端判断
4. 修正req.files的取值逻辑(req.files是数组,需遍历每个文件)
修复后的后端代码:
const express = require('express'); const app = express(); const cors = require('cors'); const cookieParser = require('cookie-parser'); const bodyParser = require('body-parser'); const multer = require('multer'); const path = require('path'); // 新增引入path模块 const db = require('./db'); // 假设你的数据库连接文件是db.js app.use(cors({ origin: ["http://localhost:3000"], methods: ["GET", "POST", "PUT", "DELETE"], credentials: true })); app.use(cookieParser()); app.use(express.json()); app.use(bodyParser.urlencoded({extended: true})); app.use(express.static("public")); const storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, "./public/images"); }, filename: (req, file, cb) => { cb(null, file.fieldname + "_" + Date.now() + path.extname(file.originalname)); } }); const upload = multer({ storage: storage }).array("files"); app.get("/media", (req, res) => { const sqlMedia = "SELECT * FROM media"; db.query(sqlMedia, (err, result) => { if(err) { return res.status(500).json({message: "查询图片失败", error: err}); } return res.json(result); }); }); app.post('/media', upload, (req, res) => { console.log('文件上传到服务器成功'); console.log(req.files); // 遍历所有上传的文件,逐个插入数据库 const insertPromises = req.files.map(file => { const filename = file.filename; const sqlImage = "INSERT INTO media (image) VALUES (?)"; return new Promise((resolve, reject) => { db.query(sqlImage, [filename], (err, result) => { if(err) reject(err); else resolve(result); }); }); }); Promise.all(insertPromises) .then(() => { return res.status(201).json({message: "所有图片上传成功"}); }) .catch(err => { return res.status(500).json({message: "图片信息存入数据库失败", error: err}); }); }); app.listen(3001, () => { console.log("服务器运行在3001端口"); });
额外注意事项
- 确保MySQL的
media表存在image字段(类型为VARCHAR,长度足够存储文件名) - 确保后端的
public/images文件夹存在,否则multer会报错 - Axios发送
multipart/form-data请求时,无需手动设置Content-Type,Axios会自动添加正确的边界标识
内容的提问来源于stack exchange,提问作者Mr. C
相关产品推荐
相关产品推荐

