通过Lambda实现Amazon S3分段上传时的分片大小与CORS问题求助
解决S3分段上传的分片大小与Lambda Payload限制冲突问题
首先,咱们先明确你遇到的两个核心矛盾:
- S3的分片规则:除最后一个分片外,所有分片必须至少5MB,你当前用2MB分片触发了
Your proposed upload is smaller than the minimum allowed size错误,这完全符合S3的规则。 - Lambda的Payload限制:同步调用Lambda的最大payload是6MB,但如果直接传二进制数据,会被自动base64编码,体积膨胀约33%——也就是说,5MB的二进制数据编码后会变成~6.6MB,超过了Lambda的限制,这就是你上传大于2MB分片时触发CORS(本质是Lambda请求被拒绝,前端抛出CORS错误)的原因。
根本解决方案:让客户端直接向S3上传分片,跳过Lambda中转
Lambda只负责初始化分段上传和完成上传这两个流程控制步骤,分片数据直接由客户端传到S3,这样既避开Lambda的payload限制,又能满足S3的分片大小要求,还能解决CORS问题。
具体步骤和代码调整:
1. 调整后端Lambda(初始化上传)
修改startMultiPartUpload Lambda,让它返回初始化上传的Key、UploadId,同时返回临时AWS凭证(通过STS AssumeRole生成),或者让Lambda直接为每个分片生成预签名URL(前端生成更灵活)。临时凭证需要具备s3:PutObject、s3:UploadPart、s3:CompleteMultipartUpload等权限。
2. 客户端代码修改(直接上传分片到S3)
替换你当前通过uploadPart Lambda中转的逻辑,改为用预签名URL直接向S3上传分片,同时修正二进制文件的处理方式(不要用text(),会破坏二进制数据并增大体积):
setLoading(true); const file = files[0]; const chunkSize = 5 * 1024 * 1024; // 设置为5MB,符合S3非最后分片的最小要求 const extension = file.name.substring(file.name.lastIndexOf('.')); try { // 1. 调用Lambda初始化分段上传,获取Key、UploadId和临时凭证 const { data: { Key, UploadId, credentials } } = await startMultiPartUpload({ fileType: extension }); // 初始化S3客户端(使用临时凭证) const s3 = new S3Client({ credentials: { accessKeyId: credentials.AccessKeyId, secretAccessKey: credentials.SecretAccessKey, sessionToken: credentials.SessionToken, }, region: '你的AWS区域(比如us-east-1)', }); let partNumber = 1; const parts = []; // 分片循环上传 for (let start = 0; start < file.size; start += chunkSize) { const end = Math.min(start + chunkSize, file.size); const chunk = file.slice(start, end); // 直接获取二进制Blob,不要转文本 // 生成当前分片的预签名URL(有效期1小时) const uploadCommand = new UploadPartCommand({ Bucket: '你的S3存储桶名称', Key, UploadId, PartNumber: partNumber, }); const signedUrl = await getSignedUrl(s3, uploadCommand, { expiresIn: 3600 }); // 直接PUT请求上传分片到S3 const uploadResponse = await fetch(signedUrl, { method: 'PUT', body: chunk, headers: { 'Content-Type': file.type, // 保持文件原始MIME类型 }, }); if (!uploadResponse.ok) { throw new Error(`分片 ${partNumber} 上传失败: ${uploadResponse.statusText}`); } // 从响应头获取ETag(注意去掉前后的引号) const eTag = uploadResponse.headers.get('ETag').replace(/"/g, ''); parts.push({ ETag: eTag, PartNumber: partNumber }); partNumber++; } // 3. 调用Lambda完成分段上传 const completeResponse = await completeMultiPartUpload({ fileKey: Key, uploadId: UploadId, parts, }); console.log('上传完成:', completeResponse); } catch (e) { console.error('上传出错:', e); } finally { setLoading(false); }
3. 配置S3存储桶的CORS规则
要让前端能直接PUT到S3,需要在S3存储桶的CORS配置中添加允许PUT请求的规则,示例配置:
<CORSConfiguration> <CORSRule> <AllowedOrigin>https://你的前端域名.com</AllowedOrigin> <!-- 不要用*,生产环境指定具体域名 --> <AllowedMethod>PUT</AllowedMethod> <AllowedMethod>POST</AllowedMethod> <AllowedMethod>GET</AllowedMethod> <AllowedMethod>HEAD</AllowedMethod> <MaxAgeSeconds>3000</MaxAgeSeconds> <AllowedHeader>*</AllowedHeader> </CORSRule> </CORSConfiguration>
额外注意事项
- 临时凭证的权限控制:确保STS角色的权限最小化,只授予必要的S3操作权限,避免安全风险。
- 分片大小的上限:S3单个分片最大支持5GB,你可以根据文件大小调整分片大小(比如大文件用100MB分片,提升上传效率)。
- 错误处理:建议添加分片上传失败后的重试逻辑,以及分段上传的中止逻辑(如果上传失败,调用AbortMultipartUpload避免S3存储无效分片)。
内容的提问来源于stack exchange,提问作者Nacho Gonzalez Ampudia
相关产品推荐
相关产品推荐

