如何用Multer上传多类型文件到MongoDB?排查500错误
问题描述
尝试通过Multer将视频、普通文件、PDF上传至MongoDB,使用Postman测试时出现500错误,怀疑是路由中配置多个Multer单文件上传中间件导致的,需解决该问题。
MongoDB模型代码
const mongoose = require("mongoose"); const FormCourse_schema = new mongoose.Schema({ FormCourse: { type: mongoose.Schema.Types.ObjectId, ref: "cards", required: true, }, date: { type: String }, file: { type: String }, video: { type: String }, pdf: { type: String }, }); module.exports = mongoose.model("FormCourse", FormCourse_schema);
上传控制器代码
exports.post_FormCourse = async (req, res, next) => { try { // if (!req.userId) return res.status(406).json({ massege: "Unauthenticated" }); const FormCourse_id = req.body.FormCourse_id; const isFormCourse = FormCourse.findById(FormCourse_id); if (isFormCourse.lenght < 1) res.status(202).json("FormCourse not found"); const course = new FormCourse({ date: req.body.date, file: req.file.path, video: req.file.path, pdf: req.file.path, }); const result = await course.save(); res.status(200).send({ result: result, request: { type: "GET", url: "localhost:3002/form/" + result._id, }, }); } catch (error) { res.status(404).json({ message: "invalid id", error: err }); } };
路由配置代码
const express = require("express"); const router = express.Router(); const FormCourses = require("../controllers/formCourses"); const checkAuth = require("../middleware/checkAuth"); const multer = require("multer"); const storageautre = multer.diskStorage({ destination: function (req, file, cb) { cb(null, "./uploads/filesautre"); }, filename: function (req, file, cb) { cb(null, new Date().toISOString().replace(/:/g, "-") + file.originalname); }, }); const fileFilterautre = (req, file, cb) => { // reject a file if (file.mimetype === "application/*") { cb(null, true); } else { cb(null, false); } }; const uploadautre = multer({ storage: storageautre, fileFilter: fileFilterautre, }); //////// const storagevideo = multer.diskStorage({ destination: function (req, file, cb) { cb(null, "./uploads/filesvideo"); }, filename: function (req, file, cb) { cb(null, new Date().toISOString().replace(/:/g, "-") + file.originalname); }, }); const fileFiltervideo = (req, file, cb) => { // reject a file if (file.mimetype === "application/video") { cb(null, true); } else { cb(null, false); } }; const uploadvideo = multer({ storage: storagevideo, fileFilter: fileFiltervideo, }); // \\\\\\ const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, "./uploads/files"); }, filename: function (req, file, cb) { cb(null, new Date().toISOString().replace(/:/g, "-") + file.originalname); }, }); const fileFilter = (req, file, cb) => { // reject a file if (file.mimetype === "application/pdf") { cb(null, true); } else { cb(null, false); } }; const upload = multer({ storage: storage, fileFilter: fileFilter, }); /*-----------------------------get all orders-----------------------------*/ router.get("/", FormCourses.get_FormCourse); /*-----------------------------post order -----------------------------*/ router.post( "/", uploadautre.single("file"), uploadvideo.single("video"), upload.single("pdf"), FormCourses.post_FormCourse ); /*-----------------------------update order-----------------------------*/ router.patch("/:courseId", checkAuth, FormCourses.patch_FormCourse); /*----------------------------delete order-----------------------------*/ router.delete("/:delete", checkAuth, FormCourses.delete_FormCourse); module.exports = router;
Express App配置代码
const express = require("express"); const app = express(); const morgan = require("morgan"); // const bodyParser = require("body-parser"); const mongoose = require("mongoose"); const cors = require("cors"); const path = require("path"); require("dotenv").config(); //-----------------------------add routers paths-----------------------------// const cardsRouter = require("./src/router/Cards"); const formRouter = require("./src/router/FormCourses"); const histoRouter = require("./src/router/Historiques"); const secondCardsRouter = require("./src/router/SecondCards"); const usersRouter = require("./src/router/Users"); //-----------------------------Connect with mongodb-----------------------------// mongoose .connect(process.env.DB_CONNECT) .then(() => console.log("mongo connected")) .catch((err) => console.log(err)); //-----------------------------middleware-----------------------------// app.use(morgan("dev")); app.use("/uploads", express.static("uploads")); app.use(express.urlencoded({ extended: true })); app.use(express.json()); app.use(cors()); app.use((req, res, next) => { res.header("Access-Control-Allow-Origin", "*"); res.header( "Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization" ); if (req.method === "OPTIONS") { res.header("Access-Control-Allow-Methods", "PUT, POST, PATCH, DELETE, GET"); return res.status(200).json({}); } next(); }); // app.use(express.static(path.join(__dirname, "public"))); //-----------------------------Routers---------------------------// app.use("/users", usersRouter); app.use("/card", cardsRouter); app.use("/form", formRouter); app.use("/histoRouter", histoRouter); app.use("/secondCardsRouter", secondCardsRouter); //-----------------------------Handling errors---------------------// app.use((req, res, next) => { const error = new Error("Not found"); res.status(404); next(error); }); app.use((error, req, res, next) => { res.status(500 | Error.status).json({ message: error.message, }); }); module.exports = app;
问题原因
不能在同一个路由上连续使用多个single()中间件,第一个single()会解析完请求文件后丢弃剩余文件数据,后续single()找不到对应字段文件会报错,同时req.file会被覆盖,最终引发500错误。
修复方案
1. 合并Multer配置为多文件处理
使用fields()替代多个single(),可同时处理多个不同字段的文件上传:
const express = require("express"); const router = express.Router(); const FormCourses = require("../controllers/formCourses"); const checkAuth = require("../middleware/checkAuth"); const multer = require("multer"); // 统一处理不同类型文件的存储和过滤 const storage = multer.diskStorage({ destination: function (req, file, cb) { // 根据字段名分配存储目录 let dest; switch(file.fieldname) { case 'file': dest = "./uploads/filesautre"; break; case 'video': dest = "./uploads/filesvideo"; break; case 'pdf': dest = "./uploads/files"; break; default: dest = "./uploads/others"; } cb(null, dest); }, filename: function (req, file, cb) { cb(null, new Date().toISOString().replace(/:/g, "-") + file.originalname); }, }); const fileFilter = (req, file, cb) => { // 根据字段名验证文件类型 let isValid = false; switch(file.fieldname) { case 'file': isValid = file.mimetype.startsWith("application/"); break; case 'video': isValid = file.mimetype.startsWith("video/"); break; case 'pdf': isValid = file.mimetype === "application/pdf"; break; } if (isValid) { cb(null, true); } else { cb(new Error(`Invalid file type for ${file.fieldname}`), false); } }; const upload = multer({ storage: storage, fileFilter: fileFilter, }); // 路由配置 router.get("/", FormCourses.get_FormCourse); // 使用fields处理多字段文件上传 router.post( "/", upload.fields([ { name: 'file', maxCount: 1 }, { name: 'video', maxCount: 1 }, { name: 'pdf', maxCount: 1 } ]), FormCourses.post_FormCourse ); router.patch("/:courseId", checkAuth, FormCourses.patch_FormCourse); router.delete("/:delete", checkAuth, FormCourses.delete_FormCourse); module.exports = router;
2. 修复控制器代码
改用req.files获取各字段的文件路径,同时修正异步操作和必填字段问题:
exports.post_FormCourse = async (req, res, next) => { try { const FormCourse_id = req.body.FormCourse_id; // 异步操作需加await const isFormCourse = await FormCourse.findById(FormCourse_id); if (!isFormCourse) { return res.status(404).json("FormCourse not found"); } const course = new FormCourse({ FormCourse: FormCourse_id, // 模型必填字段赋值 date: req.body.date, file: req.files?.file?.[0]?.path || null, video: req.files?.video?.[0]?.path || null, pdf: req.files?.pdf?.[0]?.path || null, }); const result = await course.save(); res.status(200).send({ result: result, request: { type: "GET", url: `http://localhost:3002/form/${result._id}` }, }); } catch (error) { res.status(500).json({ message: "Server error", error: error.message }); } };
3. 其他细节修正
- 视频MIME类型修正为
video/*(原配置application/video错误) - 错误处理中
err变量改为error(原变量未定义) - 确保模型必填字段
FormCourse被正确赋值
测试说明
Postman测试时选择form-data格式,添加以下字段:
- 文本字段:
date、FormCourse_id - 文件字段:
file(普通文件)、video(视频文件)、pdf(PDF文件)
内容的提问来源于stack exchange,提问作者Hi Chem
相关产品推荐
相关产品推荐

