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

React+Node.js使用Multer传图(不依赖FormData)的问题

问题分析与解决方案

你遇到的核心问题是:直接将FileList对象通过普通JSON请求发送,导致后端无法解析文件,同时Multer配置与前端传参不匹配,最终图片既没保存到服务器,也只在MongoDB中存了字符串"[object FileList]"。

下面是具体的修改步骤,全程保留你用useState管理状态的需求,仅在提交时做必要转换:

1. 前端提交逻辑修改

在提交时将useState中的状态转换为FormData(这是文件上传的必要格式,但不影响你日常用useState管理数据):

修改CreateListing组件的onSubmit函数:

const onSubmit = async (e) => {
  e.preventDefault(); // 阻止默认表单提交行为
  try {
    const formDataToSend = new FormData();
    
    // 遍历useState中的所有字段,添加到FormData
    Object.entries(formData).forEach(([key, value]) => {
      // 处理图片文件:FileList对象需要逐个添加
      if (value instanceof FileList) {
        Array.from(value).forEach(file => {
          formDataToSend.append('imgUrl', file);
        });
      } else {
        formDataToSend.append(key, value);
      }
    });

    dispatch(createListing(formDataToSend));
    toast.success('Listing saved');
    navigate(`/profile`);
  } catch (error) {
    toast.error('Could not add new listing');
  }
}

2. 前端请求头调整

删除listingService.js中手动设置的multipart/form-data请求头,axios会自动根据FormData对象设置正确的请求头(包含必要的boundary参数):

// Create new listing
const createListing = async (listingData, token) => {
  const config = {
    headers: {
      Authorization: `Bearer ${token}`,
      // 移除手动设置的content-type,axios会自动处理
    },
  }
  console.log({listingData})
  const response = await axios.post(API_URL, listingData, config)

  return response.data
}

3. 后端Multer配置修改

由于你允许上传最多6张图片,将upload.single改为upload.array,匹配前端多文件上传的场景:

修改listingRoutes.js:

// 替换原有的upload.single('imgUrl')
router.post('/', upload.array('imgUrl', 6), protect, createListing)

4. 后端Controller逻辑修改

Multer处理后的文件会存放在req.files中(数组格式),需要将文件路径存入MongoDB,而非从req.body读取无效的字符串:

修改listingController.js的createListing函数:

const createListing = asyncHandler(async (req,res) =>{
    // 从req.body获取除图片外的其他字段
    const { bathrooms,bedrooms,discountedPrice,furnished,latitude,longitude,location,name,offer,
        parking,regularPrice,timestamp,type,userRef } = req.body

    const user = await User.findById(req.user.id)
    
    if (!user){
        res.status(401)
        throw new Error('User not found')
    }

    // 提取所有上传图片的存储路径
    const imgUrls = req.files.map(file => file.path);

    const listing = await Listing.create({
        bathrooms,
        bedrooms,
        discountedPrice,
        furnished,
        imgUrl: imgUrls, // 将路径数组存入数据库
        latitude,
        location,
        longitude,
        name,
        offer,
        parking,
        regularPrice,
        timestamp,
        type,
        userRef: req.user.id
    })

    console.log({listing})
    console.log(listing)
    if (listing) {
        res.status(201).json({
          _id: listing._id,
          name: listing.name,
          message: 'House has been added'
        })
      } else {
        res.status(400)
        throw new Error('Invalid listing data')
      }
        
})

额外注意事项

  • 确保项目根目录下存在uploads文件夹,Multer不会自动创建该目录,手动创建即可。
  • 前端文件输入框的id是images,但你存到了imgUrl状态字段,这个逻辑没问题;如果想更统一,可以把输入框id改为imgUrl,但不修改也不影响功能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 12:01:19