MERN栈中Multer+Cloudinary视频上传失败:req.file读取异常
MERN栈视频上传故障排查
我在MERN栈中基于Multer和Cloudinary构建了文件上传功能,图片上传正常,但尝试上传视频时遇到以下问题:
- 初始报错:
Cannot read property 'path' of undefined
- 修改后端代码后报错变为:
Cannot read property "headers" of undefined
- 目前通过
console.log()能输出正确请求头:
{"x-powered-by":"Express","access-control-allow-origin":"*","content-type":"application/json; charset=utf-8","content-length":"2","etag":"W/"2-vyGp6PvFo4RvsFtPoIWeCReyIC8""}
现在无错误提示,但视频未上传成功,仪表盘仅显示空边框。我排查过前端到Redux切片、Redux服务的FormData,"src"字段均为对应视频文件,猜测可能是时序问题,恳请帮忙排查。
当前后端代码
const upload = require("../utils/multer"); const cloudinary = require("../utils/cloudinary"); const multer = require("multer"); const path = require("path"); const onHeaders = require("on-headers"); function responseDebugger() { console.log(JSON.stringify(this.getHeaders())) } //create router.post("/", (req,res,next)=>{ onHeaders(res, responseDebugger) res.json({}) }) router.post("/", verifyTokenAndAuthorization, async (req,res)=>{ function fileupload(req,res,next){ const uploadVideo = upload.single("src"); uploadVideo(req,res, function(err){ if(err instanceof multer.MulterError){ console.log(err); } else if(err){ console.log(err); } next(); }) } fileupload(); try{ const result = await cloudinary.uploader.upload_large(req.file.path, { upload_preset: "Mern_redux-practice", resource_type: "video", } ); const newVideos= new Videos({ cloudinary_id: result.public_id, ressort: req.body.ressort, theme: req.body.theme, title:req.body.title, src: result.secure_url, }) const savedVideos= await newVideos.save(); res.status(200).json(savedVideos); } catch(error){ res.status(403) console.log(error) throw new Error("Action failed"); } });
Multer存储配置
const maxSize = 5 * 1024 *1024; const storage = multer.diskStorage({ destination:(req,file, callback)=>{ callback(null, path.resolve(process.cwd(), 'frontside/public/uploads')); }, fileName: (req, file, callback)=>{ callback(null, Date.now()+ "--"+ path.extname(file.originalname)); console.log(req.file) }, }) const upload = multer({ storage:storage, fileFilter:(req,file,callback)=>{ if( file.mimetype == "image/png"|| file.mimetype == "image/jpg" || file.mimetype == "image/jpeg" || file.mimetype == "video/mp3" || file.mimetype == "video/mp4" ){ callback(null, true); } else{ callback(null,false) return callback(new Error("Only .png, .jpg, .jpeg, .mp3, .m4 allowed")) } }, limits:{fileSize: maxSize} });
前端HTTP请求代码
const createVideos = async (videosData, token)=>{ if(videosData){ for(let value of videosData){ console.log(value); } } const config = { headers:{ 'Content-Type': 'application/json', token: `Bearer ${token}` } } const response = await axios.post(API_URL, videosData, config); console.log(response.data); return response.data; }
故障原因及解决方案
1. 前端请求头错误
上传文件必须使用multipart/form-data格式,手动设置Content-Type: application/json会导致后端无法解析FormData中的文件。
修复:
移除手动设置的Content-Type,axios会自动根据FormData生成正确请求头:
const createVideos = async (videosData, token)=>{ if(videosData){ for(let value of videosData){ console.log(value); } } const config = { headers:{ token: `Bearer ${token}` } } const response = await axios.post(API_URL, videosData, config); console.log(response.data); return response.data; }
2. 后端路由冲突+Multer调用错误
- 两个
router.post("/")路由会导致第一个优先匹配,直接返回空JSON,带验证的业务路由永远不会执行。 fileupload()调用时未传递req/res/next参数,Multer无法获取请求对象,req.file自然为undefined。
修复:
删除无用路由,将Multer作为路由中间件直接挂载:
const upload = require("../utils/multer"); const cloudinary = require("../utils/cloudinary"); const multer = require("multer"); const path = require("path"); const onHeaders = require("on-headers"); function responseDebugger() { console.log(JSON.stringify(this.getHeaders())) } //create - 合并为单个路由,Multer作为中间件直接使用 router.post("/", verifyTokenAndAuthorization, upload.single("src"), async (req,res)=>{ onHeaders(res, responseDebugger) try{ // 先校验文件是否存在 if(!req.file) { return res.status(400).json({error: "No video file uploaded"}); } const result = await cloudinary.uploader.upload_large(req.file.path, { upload_preset: "Mern_redux-practice", resource_type: "video", }); const newVideos= new Videos({ cloudinary_id: result.public_id, ressort: req.body.ressort, theme: req.body.theme, title:req.body.title, src: result.secure_url, }) const savedVideos= await newVideos.save(); res.status(200).json(savedVideos); } catch(error){ console.log(error) res.status(403).json({error: "Action failed"}); } });
3. Multer配置错误
diskStorage中的方法名是filename(小写n),不是fileName,大小写错误会导致文件存储失败。- 5MB的大小限制对视频来说过小,建议调整到合理值(如50MB)。
video/mp3是错误的MIME类型,正确应为audio/mpeg,错误类型会导致视频被过滤。
修复:
const maxSize = 50 * 1024 *1024; // 调整为50MB const storage = multer.diskStorage({ destination:(req,file, callback)=>{ callback(null, path.resolve(process.cwd(), 'frontside/public/uploads')); }, filename: (req, file, callback)=>{ // 修正方法名 callback(null, Date.now()+ "--"+ path.extname(file.originalname)); }, }) const upload = multer({ storage:storage, fileFilter:(req,file,callback)=>{ const allowedTypes = [ "image/png", "image/jpg", "image/jpeg", "audio/mpeg", // 修正MP3的MIME类型 "video/mp4" ]; if(allowedTypes.includes(file.mimetype)){ callback(null, true); } else{ callback(new Error("Only .png, .jpg, .jpeg, .mp3, .mp4 allowed")); // 修正错误提示 } }, limits:{fileSize: maxSize} });
4. 错误处理优化
后端catch块中throw new Error无法将错误返回前端,应直接用res.status().json()返回错误信息,避免请求挂起。
内容的提问来源于stack exchange,提问作者Roman
相关产品推荐
相关产品推荐

