如何使用JSON通过Axios上传文件数组?React+TS项目问题
问题描述
我在开发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

