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

React+Node.js项目中Multer的req.file返回undefined求助

问题:Multer处理文件上传时req.file始终返回undefined

基于React+Node.js搭建图片上传功能,使用Multer处理文件上传,但后端req.file始终返回undefined,触发if(!imageFile)的错误逻辑。已尝试正确使用FormData、设置请求头、配置upload.single('image'),并移除冲突依赖,但问题未解决。

后端代码

const express = require('express');
const axios = require('axios');
const { google } = require('googleapis');
const cors = require('cors');
const multer = require('multer');
const path = require('path')
const app = express();
app.use(cors());
app.use(express.json())
app.use(express.urlencoded({ extended: true }));
const port = process.env.PORT || 3002;
const SERVICE_ACCOUNT_KEY_PATH = './filsufid-710f5c83e46e.json';

const imageStorage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'uploads')
  },
  filename: (req, file, cb) => {
    cb(null, Date.now() + path.extname(file.originalname));
  },
});

const upload = multer({
  storage: imageStorage,
  limits: {
    fileSize: 25 * 1024 * 1024, // 25 MB
  },
  fileFilter(req, file, cb) {
    if (!file.originalname.match(/\.(png|jpg)$/)) {
      return cb(new Error('Please upload a valid image file (png or jpg)'));
    }
    cb(null, true);
  },
}); 

app.post('/uploadimage', upload.single('image'), async (req, res) => {
  const imageFile = req.file;
  console.log(imageFile)
  if (!imageFile) {
    return res.status(400).json({ error: 'No image file provided' });
  }

  const auth = new google.auth.GoogleAuth({
    keyFile: SERVICE_ACCOUNT_KEY_PATH,
    scopes: ['https://www.googleapis.com/auth/drive.file'],
  });

  const drive = google.drive({ version: 'v3', auth });

  try {
    const fileMetadata = {
      name: imageFile.originalname,
      parents: ['10OuJZ4KCVMR3xciLyCn8ex0NUj9XetXX'],
    };

    const media = {
      mimeType: imageFile.mimetype,
      body: require('fs').createReadStream(imageFile.path),
    };

    const response = await drive.files.create({
      resource: fileMetadata,
      media: media,
      fields: 'id',
    });

    require('fs').unlinkSync(imageFile.path);

    res.json({ fileId: response.data.id });
  } catch (error) {
    console.error('Error uploading image to Google Drive:', error);
    res.status(500).json({ error: 'Internal Server Error' });
  }
});

前端代码

import React, { useState, useEffect } from 'react'
import Navbar from '../navbar/Navbar.js'
import Axios from 'axios';
import { useSelector } from 'react-redux';

function CreatePost() {

  const [selectedImage, setSelectedImage] = useState(null);

  const handleImageUpload = (e) => {
    const file = e.target.files[0];
    if (file) {
      const reader = new FileReader();
      reader.onload = (e) => {
        setSelectedImage(e.target.result);
      };
      reader.readAsDataURL(file);
    }
  };

  const uploadImageToGoogleDrive = () => {
    try { 
      const formData = new FormData();
      console.log(selectedImage)
      formData.append('image', selectedImage);

      const response = Axios.post(
        'http://localhost:3002/uploadimage',
        formData,
        {
          headers: {
            'Content-Type': 'multipart/form-data',
          },
        }
      );

      return response.data;
    } catch (error) {
      console.error('Error uploading image to Google Drive:', error);
      return null;
    }
  };

  return (
    <div>
  
        <Navbar/>

        <h1>Create Post</h1>
        <div>Upload Image</div>
        <input
          type="file"
          name = "image"
          onChange={(e) => handleImageUpload(e)}
        />
        {selectedImage && (
          <div>
            <h2>Preview:</h2>
            <img
              src={selectedImage}
              alt="Selected"
              style={{ maxWidth: '100%', maxHeight: '400px' }}
            />
          </div>
        )}

        <button onClick={uploadImageToGoogleDrive}>Upload Image test</button>

    </div>
  )
}

export default CreatePost

解决方案

1. 前端错误:传递的是DataURL而非原始File对象

当前前端将selectedImage设置为DataURL(base64字符串),而Multer需要接收原始的File对象才能正确解析。修改前端代码,同时保存原始文件和预览URL:

// 修改状态定义
const [selectedFile, setSelectedFile] = useState(null);
const [previewUrl, setPreviewUrl] = useState(null);

// 修改handleImageUpload
const handleImageUpload = (e) => {
  const file = e.target.files[0];
  if (file) {
    setSelectedFile(file); // 保存原始File对象
    const reader = new FileReader();
    reader.onload = (e) => {
      setPreviewUrl(e.target.result); // 预览用的DataURL
    };
    reader.readAsDataURL(file);
  }
};

// 修改uploadImageToGoogleDrive
const uploadImageToGoogleDrive = async () => {
  try { 
    const formData = new FormData();
    formData.append('image', selectedFile); // 传递原始File对象

    const response = await Axios.post(
      'http://localhost:3002/uploadimage',
      formData
      // 移除手动设置的Content-Type,Axios会自动处理
    );

    console.log(response.data);
    return response.data;
  } catch (error) {
    console.error('Error uploading image to Google Drive:', error);
    return null;
  }
};

// 修改预览部分
{previewUrl && (
  <div>
    <h2>Preview:</h2>
    <img
      src={previewUrl}
      alt="Selected"
      style={{ maxWidth: '100%', maxHeight: '400px' }}
    />
  </div>
)}

2. 移除手动设置的Content-Type请求头

Axios在上传FormData时,会自动生成包含正确boundary的Content-Type头。手动设置multipart/form-data会丢失boundary信息,导致后端Multer无法解析表单数据。

3. 确保后端uploads目录存在

Multer的diskStorage不会自动创建目标目录,若./uploads不存在,会导致文件保存失败,间接引发req.file为undefined。在后端代码中添加目录创建逻辑:

const fs = require('fs');
const uploadDir = './uploads';
// 检查并创建uploads目录
if (!fs.existsSync(uploadDir)) {
  fs.mkdirSync(uploadDir, { recursive: true }); // recursive支持创建多级目录
}

4. 验证中间件顺序

确保express.json()和express.urlencoded()在Multer路由中间件之前,当前后端代码的顺序是正确的,无需调整。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 12:44:51