为什么服务器在Postman中正常,前端上传文件却异常?
问题:Express + Multer 文件上传服务器在React前端上传非法文件时响应异常
我用Express和Multer搭建了文件上传服务器,搭配React前端实现文件提交功能。上传PDF格式的book文件和图片格式的coverImage文件时服务器运行正常,但上传不符合要求的文件类型时,服务器有时返回错误响应,有时无响应。奇怪的是,使用Postman测试时服务器始终运行正常。
服务器代码
const express = require("express"); const multer = require("multer"); const cors = require("cors"); const app = express(); app.use(cors()); //---------------------------------------- const fileStorage = multer.diskStorage({ destination: function (req, file, cb) { const fileType = file.fieldname === "coverImage" ? "images" : "books"; cb(null, fileType); // 根据文件类型指定目标目录 }, filename: function (req, file, cb) { cb(null, file.originalname); // 使用原始文件名 }, }); const validateFileTypes = (req, file, cb) => { if (file.fieldname === "book" && !file.originalname.endsWith(".pdf")) { return cb(new Error('Only PDF files are allowed for the "book" field')); } if (file.fieldname === "coverImage" && !file.mimetype.startsWith("image/")) { return cb( new Error('Only image files are allowed for the "bookCover" field') ); } // 接受文件 cb(null, true); }; app.use( multer({ storage: fileStorage, fileFilter: validateFileTypes }).fields([ { name: "book", maxCount: 1 }, // 'images'字段最多两个文件 { name: "coverImage", maxCount: 1 }, // 'documents'字段最多三个文件 ]) ); //-------------------------------------- app.post("/", (req, res) => { res.status(200).json({ status: "success", msg: "It's working!" }); }); // app.post("/upload", (req, res, next)=>{ // console.log(req.files); // console.log(req.body); // res.status(200).json({msg: "Files Uploaded Successfully!"}); // }) app.all("*", (req, res, next) => { // res.status(400).json({error: `Can't find the root you are looking for my love: ${req.url}`}) const err = new Error(`Can't find the root ${req.url} my love`); err.status = "fail"; err.statusCode = 404; next(err); }); app.use((err, req, res, next) => { err.status = err.status || "error"; err.statusCode = err.statusCode || 500; res.status(err.statusCode).json({ status: err.status, message: err.message }); next(); }); app.listen(4000);
前端代码
import { useState, useRef } from "react" export default function TestPage(){ const [messageState, setMessageState] = useState({}); const bookRef = useRef(); const coverImageRef = useRef(); const submitHandler = (event)=>{ event.preventDefault(); const fileData = new FormData(); fileData.append('book', bookRef.current.files[0]); fileData.append('coverImage', coverImageRef.current.files[0]); sendFile(fileData); } const sendFile= async (fileData) => { try { const response = await fetch("http://localhost:4000/", { method: "POST", body: fileData, }); if (!response.ok) { // 如果响应状态不正常,抛出错误 const errorResponse = await response.json(); const errorMessage = errorResponse.message || "Server error occurred"; throw new Error(`HTTP error! Status: ${response.status}. Error message: ${errorMessage}`); } const responseData = await response.json(); setMessageState(responseData); // 需要的话在这里处理成功逻辑 } catch (error) { // 处理fetch过程中出现的错误 setMessageState(error.message); // 向用户展示服务器返回的错误信息或执行其他操作 // alert(`Server Error: ${error.message}`); } }; return ( <> <form onSubmit={submitHandler}> <input type="file" ref={bookRef} /> <input type="file" ref={coverImageRef} /> <button type="submit">Submit</button> </form> <h1>Message: {JSON.stringify(messageState)}</h1> {/* {messageState.map(item => <h1 key={item}>{item}</h1>)} */} </> ) }
问题原因及修复方案
核心问题分析
- Multer全局挂载引发的错误处理异常:你通过
app.use()全局挂载了Multer中间件,这意味着所有请求(包括不需要文件上传的404请求)都会经过Multer处理。当非法文件触发fileFilter错误时,错误传递到全局处理器后,可能因为请求体未完全解析导致响应卡住。 - 前端未处理空文件场景:用户未选择文件就提交时,
files[0]为undefined,FormData会将其转为字符串"undefined",导致Multer处理逻辑异常。 - 错误处理器多余的
next()调用:全局错误处理器最后调用next(),会让Express继续寻找后续中间件,导致响应无法正常结束。
修复步骤
1. 将Multer中间件绑定到指定上传路由
移除全局的app.use(multer(...)),只在需要文件上传的路由(比如/upload)上使用:
// 删除原来的app.use(multer(...))代码 // 改为在/upload路由中挂载Multer app.post("/upload", multer({ storage: fileStorage, fileFilter: validateFileTypes }).fields([ { name: "book", maxCount: 1 }, { name: "coverImage", maxCount: 1 }, ]), (req, res, next)=>{ console.log(req.files); console.log(req.body); res.status(200).json({msg: "文件上传成功!"}); } );
同时修改前端请求URL为http://localhost:4000/upload。
2. 前端增加空文件校验
提交前检查用户是否选择了所有必填文件,避免传递无效值:
const submitHandler = (event)=>{ event.preventDefault(); const bookFile = bookRef.current.files?.[0]; const coverImageFile = coverImageRef.current.files?.[0]; if (!bookFile || !coverImageFile) { setMessageState("请选择所有必填文件"); return; } const fileData = new FormData(); fileData.append('book', bookFile); fileData.append('coverImage', coverImageFile); sendFile(fileData); }
3. 修复全局错误处理器
移除错误处理器最后的next(),确保响应正常结束:
app.use((err, req, res, next) => { err.status = err.status || "error"; err.statusCode = err.statusCode || 500; res.status(err.statusCode).json({ status: err.status, message: err.message }); // 删除这里的next()调用 });
4. 优化文件类型校验逻辑
当前book字段仅校验文件名后缀,可结合mimetype提升校验严谨性:
const validateFileTypes = (req, file, cb) => { if (file.fieldname === "book") { const isPdf = file.originalname.endsWith(".pdf") && file.mimetype === "application/pdf"; if (!isPdf) { return cb(new Error('"book"字段仅支持PDF文件')); } } if (file.fieldname === "coverImage" && !file.mimetype.startsWith("image/")) { return cb(new Error('"coverImage"字段仅支持图片文件')); } cb(null, true); };
验证效果
修改完成后测试:
- 上传合法文件:正常返回成功响应
- 上传非法文件:服务器立即返回对应错误信息
- 未选择文件提交:前端直接提示用户,不会发送无效请求
内容的提问来源于stack exchange,提问作者Official tashif
相关产品推荐
相关产品推荐

