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

使用Multer获取上传文件路径/文件名时返回undefined求助

问题描述

尝试获取文件上传至文件夹后的路径或名称,但用console.log输出req.files.path或req.files.filenames时返回undefined,以下是React和Node.js代码,求解决建议。

React JS 代码

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('');
    const [selectedFile, setSelectedFile] = useState([]);
    const [errorMessage, setErrorMessage] = useState('');
    const navigate = useNavigate();
    
    const propertyInfoHandler = () => {
        const url = 'http://localhost:3001/property/properties_upload';
        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);

        // 添加文件
        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) => {
            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(Array.from(e.target.files))} />
                </div>
                <h2>{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 app = express();
const SqlDbStore = require('express-mysql-session')(session);
const passport = require('passport');
const cookieParser = require('cookie-parser');
const multer = require('multer');
//----------------------------------------- BEGINNING OF PASSPORT MIDDLEWARE AND SETUP ---------------------------------------------------
app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(passport.session());
app.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 multerDestination = './routes/Images/';
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, multerDestination);
  },
  filename: function (req, file, cb) {
    cb(null, Date.now() + '-' + file.originalname );
  }
});
const upload = multer({ storage: storage }).array('files');
//----------------------------------------- END OF PASSPORT MIDDLEWARE AND SETUP ---------------------------------------------------
router.post('/properties_upload', (req, res) => {
  // 修正req.body字段名,对应FormData里的键
  const address = req.body.addressLine;
  const address2 = req.body.addressLine2;
  const city = req.body.addressCity;
  const state = req.body.addressState;
  const addressZipCode = req.body.addressZipCode;
  const mlsID = req.body.mlsID;
  const rentalID = Math.floor(Math.random()*90000) + 10000;

  upload(req, res, function (err) {
    if (err instanceof multer.MulterError) {
        return res.status(500).json(err)
    } else if (err) {
        return res.status(500).json(err)
    }
    // req.files是数组,遍历每个文件获取路径和文件名
    if(req.files && req.files.length > 0) {
      req.files.forEach(file => {
        console.log("文件路径: ", file.path);
        console.log("文件名: ", file.filename);
      });
    }
    return res.status(200).send(req.files)
  });
});

module.exports = router;

关键修正点

  1. React端:
    • selectedFile初始值改为数组[],将e.target.files转换为数组方便遍历处理
    • 将地址等属性参数添加到FormData中,而非Axios的配置项(Axios第三个参数是请求配置,不是请求体)
    • 显式设置multipart/form-data请求头,确保后端正确解析表单数据
  2. Node端:
    • 修正req.body字段名,与前端FormData的键对应(比如req.body.city改为req.body.addressCity)
    • multer的diskStorage回调参数名改为file(单数),避免语义混淆
    • req.files是数组类型,需遍历每个文件对象获取path(本地存储路径)和filename(存储后的文件名)属性

内容的提问来源于stack exchange,提问作者JonD

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 08:40:40