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
相关产品推荐
相关产品推荐

