React+Serverless+API Gateway解析FormData上传S3文件损坏求助
React + Serverless API Gateway + Lambda 上传文件到S3损坏问题
我尝试通过React前端,借助Serverless框架搭建的API Gateway与Lambda函数,将文件和数据上传至S3存储桶,已困扰多日。
前端代码
前端使用axios构造FormData发送POST请求:
let formData = new FormData(); formData.append('imageFile', selectedImage); formData.append('itemId', clubIdRef.current.value); formData.append('itemDescription', itemDescRef.current.value); axios.post( baesURL+"/item/create", formData, {headers: { 'Content-Type': 'multipart/form-data' }} ).then((response) => { console.log("response" + response) console.log("response.data" + response.data) })
虽然将字符串属性加入FormData感觉不太合理,但这是我找到的同时传输数据和图片的唯一方式。
后端解析情况
后端使用lambda-multipart-parser解析数据:
const createItem = async (event) => { const result = await multipartParser.parse(event); const imageFile = result.imageFile; const itemDescription = result.itemDescription;
解析结果打印为:
{ files: [], imageFile: '[object File]', itemId: '12', itemDescription: "Description" }
S3上传问题
我能将imageFile存入S3并生成URL,再结合URL、id和description创建Item对象存入DynamoDB,流程看似正常,但打开S3 URL时文件损坏,仅显示灰色方框。S3上传代码如下:
const AWS = require("aws-sdk"); const s3 = new AWS.S3(); const params = { Bucket: BUCKET_NAME, Key: `images/${directoryPath}/${id}.png`, Body: imageFile, ContentType: "image/png", ACL : "public-read" } uploadResult = await s3.putObject(params).promise();
已尝试的无效方法
- 尝试修改API Gateway的BinaryMediaType,但找不到对应设置;
- 使用
aws-lambda-multipart-parser,仍无法添加multipart/form-data二进制媒体类型并正确解析完整FormData。
我知道可以通过React直接调用aws-sdk获取预签名URL上传图片,再将URL通过POST请求发送至API Gateway解析event.body,但希望避免多请求,在后端完成全部处理。
解决方案
核心问题
解析后的imageFile是字符串[object File],说明multipart解析器未正确处理二进制文件,仅将文件对象转为字符串,导致S3存储的是无效内容。
步骤1:配置API Gateway支持二进制传输
- 进入API Gateway控制台,选择你的API;
- 切换到Settings(设置)选项卡;
- 在Binary Media Types中添加
multipart/form-data; - 重新部署API(必须部署才会生效)。
步骤2:更换并正确使用解析库
使用aws-lambda-multipart-parser(支持二进制解析),修改Lambda代码:
const multipartParser = require('aws-lambda-multipart-parser'); const createItem = async (event) => { // 解析带二进制的FormData,第二个参数true表示处理base64编码的body const result = await multipartParser.parse(event, true); // 从files数组中获取上传的文件 const imageFile = result.files.find(file => file.fieldname === 'imageFile'); const itemId = result.itemId; const itemDescription = result.itemDescription; // 上传S3时使用文件的二进制内容 const AWS = require("aws-sdk"); const s3 = new AWS.S3(); const params = { Bucket: BUCKET_NAME, Key: `images/${directoryPath}/${itemId}.png`, Body: imageFile.content, // 取文件的二进制Buffer ContentType: imageFile.contentType, // 用解析到的真实文件类型,不要硬编码 ACL: "public-read" }; const uploadResult = await s3.putObject(params).promise(); // 后续存入DynamoDB的逻辑... };
步骤3:前端代码优化
去掉手动设置的Content-Type,axios会自动生成包含boundary的正确头:
axios.post( baesURL+"/item/create", formData ).then((response) => { console.log("response", response); console.log("response.data", response.data); })
验证
- 重新部署API Gateway;
- 测试请求后,检查Lambda中
result.files是否包含完整的文件对象(含content、contentType属性); - 查看S3文件大小是否与原文件一致,一致则上传成功。
内容的提问来源于stack exchange,提问作者ph-quiett
相关产品推荐
相关产品推荐

