You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

电商项目中Postman上传图片至MongoDB失败问题求助

商品添加时Multer无法接收上传文件,req.files始终为空

电商网站添加商品时,商品详情能正常存入MongoDB,但图片字段始终是空数组。已配置Multer处理多文件上传,用Postman选择本地图片上传,但req.files始终为空,确认Multer中间件已执行,重启服务器也没用。调试时在addProduct里打印req.files,输出是[Object: null prototype] {},本地文件存在,Postman上传路径也没问题,求排查问题。


相关代码文件

productController.js

import { v2 as cloudinary } from "cloudinary"
import productModel from "../models/productModel.js"

//function for adding product
const addProduct = async (req, res) => {
    try {
        //getting product details
        const  { name, description, price, category, subCategory, sizes, bestseller } = req.body

        //getting product images
        const image1 = req.files.image1 && req.files.image1[0]
        const image2 = req.files.image2 && req.files.image2[0]
        const image3 = req.files.image3 && req.files.image3[0]
        const image4 = req.files.image4 && req.files.image4[0]

        const images = [image1, image2, image3, image4].filter((item)=> item !== undefined)

        let imagesUrl = await Promise.all(
            images.map(async (item) => {
                let result = await cloudinary.uploader.upload(item.path,{resource_type: 'image'});

                return result.secure_url
            })
        )

        const productData = {
            name,
            description,
            category,
            price: Number(price),
            subCategory,
            bestseller: bestseller === "true" ? true : false,
            sizes: JSON.parse(sizes),
            image: imagesUrl,
            date: Date.now()
        }

        console.log(productData);
        
        const product = new productModel(productData);
        await product.save()

        res.json({success:true, message:"Product Added"})
        
        
    } catch (error) {

        console.log(error)
        
        res.json({success:false, message:error.message})

    }
}

//function for listing products
const listProducts = async (req, res) => {

}

//function for removing product
const removeProduct = async (req, res) => {

}

//function for single product info
const singleProduct = async (req, res) => {

}

export {listProducts, addProduct, removeProduct, singleProduct}

multer.js

import multer from "multer";

const storage = multer.diskStorage({
   
    filename:function(req, file,callback){
        callback(null, file.originalname)
    }
})

const upload = multer({storage})

export default upload

productModel.js

import mongoose from "mongoose";

const productSchema = new mongoose.Schema({
    name: {type: String, required: true},
    description: {type :String, required: true},
    price: {type: Number, required: true},
    image: {type: Array, required:true},
    category: {type: String, required:true},
    subCategory: {type: String, required:true},
    sizes: {type: Array, required:true},
    bestseller: {type: Boolean},
    date: {type: Number, required:true},
    
})

const productModel = mongoose.models.product || mongoose.model("product", productSchema)
// ^^ If the product model is available, it gets assigned to the variable. Else, a new model is created. 

export default productModel

productRoute.js

import express from 'express'

import {listProducts, addProduct, removeProduct, singleProduct} from '../controllers/productController.js'
import upload from '../middleware/multer.js';

const productRouter = express.Router();

productRouter.post('/add',
 upload.fields([
{name:'image1', maxCount:1}, {name:'image2', maxCount:1}, {name:'image3', maxCount:1},
{name:'image4', maxCount:1}
]), addProduct);
productRouter.post('/remove',removeProduct);
productRouter.post('/single',singleProduct);
productRouter.get('/list',listProducts)

export default productRouter

排查方向与解决方案

1. Postman请求格式校验

  • 必须使用form-data格式发送请求,禁止用raw/x-www-form-urlencoded等格式
  • 每个文件字段的key必须严格匹配代码中的image1/image2/image3/image4,大小写完全一致
  • 确保Postman中每个文件字段的类型设置为File,而非Text

2. 补全Multer存储配置

当前multer.js仅设置了filename,缺少destination(文件保存目录),可能导致文件无法写入,Multer无法识别:

import multer from "multer";
import path from 'path';
import fs from 'fs';

// 自动创建uploads目录(不存在时)
const uploadDir = path.join(process.cwd(), 'uploads');
if (!fs.existsSync(uploadDir)) {
  fs.mkdirSync(uploadDir, { recursive: true });
}

const storage = multer.diskStorage({
  destination: function(req, file, callback) {
    callback(null, uploadDir); // 指定文件保存路径
  },
  filename:function(req, file,callback){
    // 添加时间戳避免文件名重复覆盖
    callback(null, Date.now() + '-' + file.originalname);
  }
})

const upload = multer({storage})

export default upload

3. 调整Express中间件执行顺序

确保文件上传路由**优先于express.json()/express.urlencoded()**执行,否则这类解析body的中间件会消耗请求体,导致Multer无法读取文件:

// 正确顺序
app.use('/api/products', productRouter); // 先处理文件上传路由
app.use(express.json()); // 再处理普通JSON请求

4. 增大文件大小限制

Multer默认单文件大小限制为1MB,若上传图片超过此大小会被静默拒绝,可显式设置更大限制:

const upload = multer({
  storage,
  limits: { fileSize: 5 * 1024 * 1024 } // 设置为5MB
})

5. 增加Cloudinary上传容错

即使文件上传成功,Cloudinary上传失败也会导致图片数组为空,建议添加错误捕获:

let imagesUrl = await Promise.all(
  images.map(async (item) => {
    try {
      let result = await cloudinary.uploader.upload(item.path,{resource_type: 'image'});
      return result.secure_url
    } catch (cloudError) {
      console.error('Cloudinary上传失败:', cloudError);
      return null;
    }
  })
).then(urls => urls.filter(url => url !== null)); // 过滤上传失败的图片

内容的提问来源于stack exchange,提问作者Bhargav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 04:23:09