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

如何使用JSON通过Axios上传文件数组?React+TS项目问题

能否用JSON而非FormData上传图片数组?

问题描述

我在开发React+TypeScript项目,用Axios向服务器发送图片数组。想知道能不能用JSON代替FormData上传这些文件?现在我设置名为images的数组时,服务器端收到的是images.0、images.1这类结构,解构获取images得到undefined。请问有没有用JSON上传文件数组的方法?

前端请求代码

export const createProduct = async (product: IProductCreate) => {
  const { data } = await $authHost.post<IProductCreate>('api/product', product, {
    headers: {
      'Content-Type': 'multipart/form-data'
    }
  })
  return data
}

await createProduct({
      description,
      price,
      gender: JSON.stringify(gender),
      images: images,
      Colors: JSON.stringify(colors),
      BrandId,
      CategoryId
    })

服务器端代码(Express+Sequelize+TypeScript)

async create (req: productCreateRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      ...
      if (!req.files) return next(ApiError.internal('Images are not uploaded'))
      const { images } = req.files // 只能拿到images.0、images.1这类字段,解构images得到undefined

      const fileNames: string[] = []
      if (Array.isArray(images)) {
        images.forEach((el, index) => {
          fileNames[index] = uuidv4() + '.png'
          el.mv(path.resolve('src/static/' + fileNames[index]))
        })
      } else {
        fileNames[0] = uuidv4() + '.png'
        images.mv(path.resolve('src/static/' + fileNames[0]))
      }

      const product = await Product.create({
        CategoryId,
        BrandId,
        price,
        description,
        gender: JSON.stringify(parsedGender),
        images: JSON.stringify(fileNames)
      })

      res.json(product)
    } catch (error) {
      ...
    }
  }

解答

核心结论

不能直接用JSON上传文件。JSON是纯文本格式,无法包含二进制的File对象——浏览器会自动把File序列化为空对象{}或直接丢失数据,根本无法传递到服务器。

你当前问题的原因

你虽然设置了Content-Type: multipart/form-data,但直接把包含File数组的对象传给Axios时,Axios内部会把数组拆分为images.0、images.1这类独立键值对,导致服务器端req.files.images不存在,只能拿到单个索引字段。

正确处理方式(用FormData但正确构造)

必须手动构造FormData,将数组中的每个File对象以相同键名images添加进去,这样服务器端的文件解析中间件会自动将其拼接为数组:

前端代码修改

export const createProduct = async (product: IProductCreate) => {
  const formData = new FormData();
  // 添加非文件字段
  formData.append('description', product.description);
  formData.append('price', product.price.toString());
  formData.append('gender', JSON.stringify(product.gender));
  formData.append('Colors', JSON.stringify(product.Colors));
  formData.append('BrandId', product.BrandId.toString());
  formData.append('CategoryId', product.CategoryId.toString());
  
  // 遍历图片数组,统一用'images'作为键名添加
  product.images.forEach(image => {
    formData.append('images', image);
  });

  // 无需手动设置Content-Type,Axios会自动添加带boundary的正确头
  const { data } = await $authHost.post<IProductCreate>('api/product', formData);
  return data;
}

// 调用时无需提前stringify字段,直接传入原始值
await createProduct({
  description,
  price,
  gender,
  images,
  Colors: colors,
  BrandId,
  CategoryId
})

服务器端注意事项

确保使用的文件解析中间件(比如express-fileupload)开启了parseNested: true配置,这样才能正确识别数组形式的文件字段。如果使用multer,则需要设置name: 'images'来接收数组。

修改后,你现有的服务器代码中const { images } = req.files就能正常拿到文件数组,无需额外修改。

为什么不推荐用JSON传文件?

即使你把File转成Base64字符串再用JSON传递,虽然技术上可行,但会让文件体积增大约30%,且处理流程比FormData复杂得多,完全没有必要。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 18:20:58