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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 20:24:56