使用react-hook-form上传文件时req.file为空的问题求助
问题:FormData上传文件时req.file为空,返回“No files uploaded”错误
我尝试结合react-hook-form与验证系统,通过append()构建FormData,再用fetch请求将数据存入MongoDB,但始终出现“No files uploaded”错误,后端req.file为空或未定义。以下是相关代码,预期能正确构建数据对象并POST至MongoDB表中。
前端ManageRestaurantForm.jsx代码
import React from 'react' import { FormProvider, useForm } from 'react-hook-form' // PAGES import DetailsSection from './DetailsSection' import CuisinesSection from './CuisinesSection' import MenuSection from './MenuSection' import ImageSection from './ImageSection' import { forumFill } from '../../fetch/Fetch' const ManageRestaurantForm = () => { const formMethods = useForm(); const { handleSubmit } = formMethods; const onSubmit = handleSubmit(async (formDataJson) => { try { const formData = new FormData(); formData.append("restaurantName", formDataJson.restaurantName); formData.append("city", formDataJson.city); formData.append("country", formDataJson.country); formData.append( "deliveryPrice", (formDataJson.deliveryPrice * 100).toString()); formData.append( "estimatedDeliveryTime", formDataJson.estimatedDeliveryTime.toString()); formData.append("cuisines", formDataJson.cuisines) formDataJson.menuItems.forEach((menuItem, index) => { formData.append(`menuItems[${index}][name]`, menuItem.name); formData.append( `menuItems[${index}][price]`, (menuItem.price * 100).toString() ); }); console.log(formDataJson.menuItems) formDataJson.menuItems.forEach((menuItem, index) => { formData.append(`menuItems[${index}][name]`, menuItem.name); formData.append( `menuItems[${index}][price]`, (menuItem.price * 100).toString() ); }); if (formDataJson.imageFiles && formDataJson.imageFiles.length > 0) { Array.from(formDataJson.imageFiles).forEach((file, index) => { formData.append(`imageFiles[${index}]`, file); }); } console.log(formData) await forumFill("http://localhost:7000/api/my/restaurant", formData); } catch (err) { console.log(err) } }) return ( <div> <FormProvider {...formMethods}> <form onSubmit={onSubmit}> {/* FORM COMPONENTS */} <DetailsSection /> <CuisinesSection/> <MenuSection /> <ImageSection /> <span className='flex justify-end'> <button type="submit" className='bg-red-600 text-white h-full mb-10 w-40 rounded hover:rounded-md hover:duration-300 duration-300 p-2 font-bold hover:bg-red-500 cursor-pointer text-2xl'> Save </button> </span> </form> </FormProvider> </div> ) } export default ManageRestaurantForm
forumFill函数代码
// forms/manage-restaurant export const forumFill = async (url, data) => { try { const formDataJSON = JSON.stringify(data); const rawResponse = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: formDataJSON }); const content = await rawResponse.json(); console.log(formDataJSON) console.log(content); } catch (error) { console.log(error) } };
后端控制器代码
const cloudinary = require("cloudinary").v2; const mongoose = require("mongoose"); const { Restaurant } = require("../mongodb/models/restaurant"); class Controller { async createMyRestaurant(req, res) { try { if (!req.file || req.file.length === 0) { return res.status(400).json({ message: "No files uploaded" }); } const existingRestaurant = await Restaurant.findOne({ user: req.userId }); if (existingRestaurant) { return res.status(409).json({ message: "User restaurant already exists" }); } const imageFile = req.File; console.log(`Your imageFile is ${imageFile}`) const b64 = Buffer.from(imageFile.buffer).toString("base64"); const dataURI = "data:" + imageFile.mimetype + ";base64," + b64; const uploadResponse = await cloudinary.uploader.upload(dataURI); const imageUrl = uploadResponse.url; console.log(`Your imageUrl is ${imageUrl}`) const restaurant = new Restaurant({ body: req.body, lastUpdate: new Date(), }); await restaurant.save(); res.status(201).send(restaurant); } catch (err) { console.log(err); res.status(500).json({ message: "Something went wrong :(" }); } } } module.exports = Controller;
问题根源及修复方案
1. Fetch请求错误处理FormData
你在forumFill函数里把FormData转成JSON字符串,还手动设置Content-Type: application/json,但FormData必须直接作为请求体发送,浏览器会自动添加正确的multipart/form-data头。
修改后的forumFill函数:
export const forumFill = async (url, data) => { try { const rawResponse = await fetch(url, { method: 'POST', // 移除手动设置的Content-Type,浏览器自动处理 body: data }); const content = await rawResponse.json(); console.log(content); } catch (error) { console.log(error); } };
2. 后端文件获取与检查错误
- 后端代码里写了
req.File(大写F),正确应该是req.file(小写); - 前端上传的是多文件(
imageFiles[${index}]),后端需要用req.files获取,而不是req.file; - 文件检查逻辑要对应多文件场景。
3. 前端冗余代码
代码中重复循环menuItems添加到FormData,删掉其中一次即可。
4. 后端Restaurant对象构建错误
直接传body: req.body不符合模型字段规范,需要展开req.body的字段,同时关联用户ID和图片链接。
5. 后端需配置Multer处理文件上传
必须用multer中间件解析multipart/form-data请求,示例配置:
const multer = require('multer'); // 内存存储,方便转base64上传到Cloudinary const upload = multer({ storage: multer.memoryStorage() }); // 路由中使用,接收名为imageFiles的多文件 router.post('/api/my/restaurant', upload.array('imageFiles'), controller.createMyRestaurant);
修改后的后端控制器关键代码
async createMyRestaurant(req, res) { try { // 检查多文件是否存在 if (!req.files || req.files.length === 0) { return res.status(400).json({ message: "No files uploaded" }); } const existingRestaurant = await Restaurant.findOne({ user: req.userId }); if (existingRestaurant) { return res.status(409).json({ message: "User restaurant already exists" }); } // 批量上传图片到Cloudinary const uploadPromises = req.files.map(file => { const b64 = Buffer.from(file.buffer).toString("base64"); const dataURI = `data:${file.mimetype};base64,${b64}`; return cloudinary.uploader.upload(dataURI); }); const uploadResponses = await Promise.all(uploadPromises); const imageUrls = uploadResponses.map(item => item.url); // 正确构建Restaurant实例 const restaurant = new Restaurant({ ...req.body, user: req.userId, imageUrls: imageUrls, // 假设模型中有imageUrls字段存储图片链接 lastUpdate: new Date(), }); await restaurant.save(); res.status(201).send(restaurant); } catch (err) { console.log(err); res.status(500).json({ message: "Something went wrong :(" }); } }
内容的提问来源于stack exchange,提问作者With Me
相关产品推荐
相关产品推荐

