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

React结合Multer上传图片失败:图片未存入指定文件夹

React上传图片至Multer配置的Node.js服务器失败问题排查

我正尝试通过React向配置了Multer的Node.js服务器上传图片,已在前端调用API,后端完成Multer配置,但代码无法正常运行,图片并未上传至指定文件夹。请帮忙排查问题。

前端React代码

import React, { useEffect, useState, useRef } from 'react';
import { Router, useNavigate } from 'react-router-dom';
import axios from "axios";

const Dashboard = () => {
  const [userName, userNameSetter] = useState("");
  const [userEmail, userEmailSetter] = useState("");
  const [artTitle, setArtTitle] = useState("");
  const [artContent, setArtContent] = useState("");
  const [imagePreview, setPreview] = useState(undefined);
  const [imageToSend, setImageToSend] = useState(undefined);

  const fileUploadRef = useRef(null);

  async function makePost(e) {
    // 修复拼写错误:preventdefault -> preventDefault
    e.preventDefault();
    console.log("触发上传函数");
    const fd = new FormData();

    // 修复:传入原始文件对象而非本地预览URL
    fd.append("form-image", imageToSend);
    try {
      // 修复:直接传递FormData,配置正确请求头
      const response = await axios.post(
        "http://localhost:1337/api/multer-photo-upload",
        fd,
        { headers: { 'Content-Type': 'multipart/form-data' } }
      );
      console.log(response);
    } catch (error) {
      console.error("上传失败:", error);
    }
  }

  const addPostImage = () => {
    fileUploadRef.current.click();
  };

  async function uploadPostImage(e) {
    if (e.target.files.length === 0) {
      console.log("请选择一张图片");
    } else {
      const imageFile = e.target.files[0];
      const imageMain = URL.createObjectURL(imageFile);
      setPreview(imageMain);
      // 修复:存储文件对象而非预览URL
      setImageToSend(imageFile);
    }
  }

  async function performUpload(fd) {}

  // 修复:添加依赖数组,避免不必要的重复执行
  useEffect(() => {
    let emailAdd = localStorage.getItem("email");
    let uName = localStorage.getItem("name");
    userNameSetter(uName);
    userEmailSetter(emailAdd);
  }, []);

  return (
    <>
      <Header />
      <section className='dashboard-wrapper'>
        <section className='dashboard-left'>
          <center><h1>CW ADMIN DASHBOARD</h1></center>
          <center>
            <span className='u-av'>
              <i className="ri-user-fill"></i>
            </span>
            <h3>
              Welcome {userName}. <br />Your Email is {userEmail}
            </h3>
          </center>
        </section>

        <section className='dashboard-right'>
          <h3>UPLOAD AN ARTICLE</h3>
          <form action='' enctype='multipart/form-data' name='upload-article-form'>
            <input
              type='text'
              placeholder='Article Title'
              className='art-title'
              onChange={(e) => setArtTitle(e.target.value)}
            /><br />
            <textarea
              placeholder='Article Content'
              className='art-content'
              onChange={(e) => setArtContent(e.target.value)}
            ></textarea><br />

            <div className='image-preview'>
              {imagePreview && <img src={imagePreview} style={{ maxWidth: "200px" }} />}
            </div>

            <input type='button' value='Add Image' onClick={addPostImage} />
            <input
              type='file'
              name='image'
              id="img-upload"
              className='img-upload'
              ref={fileUploadRef}
              onChange={uploadPostImage}
            />
            <input type='submit' value='Upload' onClick={makePost} />
          </form>
        </section>
      </section>
      <WhiteFooter />
    </>
  );
};

export default Dashboard;

后端Node.js代码

const express = require("express");
const app = express();
const cors = require("cors");
const User = require('./models/user.model.js');
const fs = require("fs");
const path = require("path");
const multer = require("multer");

// 修复:使用绝对路径,确保上传目录存在
const uploadPath = path.resolve(__dirname, "../carefu_watchers/src/uploads/");
// 自动创建不存在的上传目录
if (!fs.existsSync(uploadPath)) {
  fs.mkdirSync(uploadPath, { recursive: true });
}

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, uploadPath);
  },
  filename: (req, file, cb) => {
    console.log(file);
    cb(null, Date.now() + path.extname(file.originalname));
  }
});

const uploadImage = multer({
  storage: storage,
  // 可选:添加文件类型过滤,仅允许图片上传
  fileFilter: (req, file, cb) => {
    const allowedTypes = /jpeg|jpg|png|gif/;
    const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
    const mimetype = allowedTypes.test(file.mimetype);
    if (extname && mimetype) {
      return cb(null, true);
    } else {
      cb(new Error("仅支持JPG/PNG/GIF格式的图片"));
    }
  }
});

// 修复:启用CORS中间件,允许跨域请求
app.use(cors());
app.use(express.json());

// 修复:添加错误处理,明确上传状态
app.post("/api/multer-photo-upload", uploadImage.single("form-image"), (req, res) => {
  if (!req.file) {
    return res.status(400).send("未接收到上传文件");
  }
  console.log("文件上传成功:", req.file);
  res.send("文件上传成功");
}, (error, req, res, next) => {
  res.status(400).send(error.message);
});

app.listen(1337, () => {
  console.log("服务器启动于端口1337");
});

关键错误点总结

  • 前端:

    1. 表单提交事件方法拼写错误:e.preventdefault() → e.preventDefault(),导致页面默认刷新中断请求
    2. 存储错误的数据:将预览URL存入状态而非原始文件对象,导致FormData无法正确传递文件
    3. axios请求参数错误:将FormData包裹在对象中传递,后端无法解析,需直接传入FormData并配置请求头
    4. useEffect缺少依赖数组,导致组件重复渲染时重复执行逻辑
  • 后端:

    1. 未启用CORS中间件,跨域请求被浏览器拦截
    2. 上传路径使用相对路径,可能因服务器启动目录不同导致路径错误,需改用绝对路径并确保目录存在
    3. 缺少错误处理逻辑,无法排查上传失败的具体原因

内容的提问来源于stack exchange,提问作者Eluan David Harold

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 19:31:23