MERN+TypeScript应用中React表单图片上传失败求助
React + TypeScript 图片上传失败问题解决
我用TypeScript开发React前端,后端基于Express(TypeScript)+Mongoose搭建,通过ThunderClient测试图片上传API完全正常,但在React表单中上传图片完全失效,尝试多种方法仍未解决,相关代码如下:
前端代码
import axios from "axios"; import { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; const ProductCreate = () => { const [title, setTitle] = useState<string>(""); const [price, setPrice] = useState<string>(""); const [ImageFile, setImageFile] = useState<File | null>(null); const [category, setCategory] = useState<string>(""); const [description, setDescription] = useState<string>(""); const [isLoading, setLoading] = useState<boolean>(false); const [isError, setError] = useState<boolean>(false); const [data, setData] = useState<[]>([]); const navigate = useNavigate(); interface handleCategory { _id: string; Name: string; } useEffect(() => { const fetchCategory = async () => { try { const res = await axios.get("http://localhost:5000/category"); await setData(res.data); } catch (error) { console.log(error); } }; fetchCategory(); }, []); // Submitting the form const handleSubmit = async (e: any) => { e.preventDefault(); const body = { title, price, description, category, ImageFile, }; console.log(body); try { const res = await axios.post( "http://localhost:5000/product/create", body, ); await res.data; navigate("/products"); console.log(res.data); } catch (err: any) { setError(err); console.log(err); } }; return ( <div className='productCreate'> {isError && <p> Something is wrong</p>} <div> <form className='prForm' onSubmit={handleSubmit} encType='multipart/form-data' > <h3> Create Product</h3> <div className='Form_Control'> <label>Product Title</label> <input onChange={(e) => setTitle(e.target.value)} type='text' placeholder='Input title' /> </div> <div className='Form_Control'> <label>Product Image</label> <input type='file' name='image' onChange={(e) => setImageFile(e.target.files ? e.target.files[0] : null) } /> </div> <div className='Form_Control'> <label>Price</label> <input onChange={(e) => setPrice(e.target.value)} type='text' placeholder='Input price' /> </div> <div className='Form_Control'> <label>Product Category</label> <select onChange={(e) => setCategory(e.target.value)} id=''> {data.map((data: handleCategory) => ( <option key={data._id} value={data._id}> {data.Name} </option> ))} </select> </div> <div className='Form_Control'> <label>Description</label> <textarea onChange={(e) => setDescription(e.target.value)} /> </div> <button type='submit'> Create</button> </form> </div> </div> ); }; export default ProductCreate;
后端路由代码
import { Router } from "express"; import { getAllProduct, getProductByID, createProduct, countProduct, updateProduct, deleteProduct, } from "../controller/productController"; import multer from "multer"; import path from "path"; const router = Router(); const storage = multer.diskStorage({ destination: (req, file, cb) => cb(null, "./upload"), filename: (req, file, cb) => { console.log(file); cb(null, Date.now() + path.extname(file.originalname)); }, }); const upload = multer({ storage }); router.get("/", getAllProduct); router.get("/:id", getProductByID); router.get("/count", countProduct); router.post("/create", upload.single("ImageFile"), createProduct); router.put("/:id", upload.single("ImageFlie"), updateProduct); router.delete("/:id", deleteProduct); export default router;
后端控制器代码
import { Request, Response } from "express"; import product from "../model/productModel"; // Get all product const getAllProduct = async (req: Request, res: Response) => { try { const findproduct = await product.find().populate("category").exec(); res.status(200).json(findproduct); } catch (err: any) { res.status(500).json(err.message); } }; // Count all product const countProduct = async (req: Request, res: Response) => { const count = await product.find().count().exec(); console.log(count); res.status(200).json({ count }); }; const getProductByID = async (req: Request, res: Response) => { try { const id = req.params.id; const getSingleProduct = await product.findById(id); res.status(200).json(getSingleProduct); } catch (err: any) { res.status(500).json(err.message); } }; const createProduct = async (req: Request, res: Response) => { try { const newProducs = new product({ title: req.body.title, description: req.body.description, price: req.body.price, category: req.body.category, image: req.file?.filename, }); const savedProduct = await newProducs.save(); res.status(200).json(savedProduct); } catch (err: any) { res.status(500).json(err.message); } }; const updateProduct = async (req: Request, res: Response) => { try { const id = req.params.id; const result = await product.findByIdAndUpdate( id, { $set: req.body }, { new: true }, ); res.status(200).json(result); } catch (err: any) { res.status(500).json(err.message); } }; const deleteProduct = async (req: Request, res: Response) => { try { const id = req.params.id; const deletedProduct = await product.findByIdAndDelete(id); res.status(200).json("Deleted Successfully"); } catch (err: any) { res.status(500).json(err.message); } }; export { getAllProduct, getProductByID, countProduct, createProduct, updateProduct, deleteProduct, };
问题修复方案
1. 前端核心修改
上传文件必须使用FormData对象封装数据,同时保证文件字段名与后端一致:
// 修改后的handleSubmit函数 const handleSubmit = async (e: any) => { e.preventDefault(); // 创建FormData对象 const formData = new FormData(); // 追加表单字段 formData.append("title", title); formData.append("price", price); formData.append("description", description); formData.append("category", category); // 追加文件,字段名必须和后端multer配置的"ImageFile"一致 if (ImageFile) { formData.append("ImageFile", ImageFile); } try { const res = await axios.post( "http://localhost:5000/product/create", formData // axios会自动识别FormData并设置正确的Content-Type,无需手动设置 ); navigate("/products"); console.log(res.data); } catch (err: any) { setError(true); console.log(err); } }; // 同时修改文件输入框的name属性,和后端保持一致 <input type='file' name='ImageFile' // 从"image"改为"ImageFile" onChange={(e) => setImageFile(e.target.files ? e.target.files[0] : null) } />
2. 后端路由笔误修复
update路由中upload.single("ImageFlie")存在拼写错误,改为ImageFile:
router.put("/:id", upload.single("ImageFile"), updateProduct);
3. 额外检查
确保后端./upload目录存在,multer不会自动创建目录,若不存在需手动创建,否则会报错。
内容的提问来源于stack exchange,提问作者clemon ezeh
相关产品推荐
相关产品推荐

