You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Express+Axios多图上传至MySQL数据库问题求助

问题排查与修复方案

核心问题总结

  1. 前后端响应状态码不匹配,导致前端误判为错误
  2. 后端处理多文件上传时,错误获取文件名(req.files是数组而非单个对象)
  3. 前端状态管理混乱:同一files状态同时存储用户选择的文件对象和数据库返回的图片数据
  4. 图片访问路径错误:静态文件托管在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端口");
});

额外注意事项

  1. 确保MySQL的media表存在image字段(类型为VARCHAR,长度足够存储文件名)
  2. 确保后端的public/images文件夹存在,否则multer会报错
  3. Axios发送multipart/form-data请求时,无需手动设置Content-Type,Axios会自动添加正确的边界标识

内容的提问来源于stack exchange,提问作者Mr. C

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 16:23:16