如何在不共享密钥的情况下实现AWS S3分片上传?
嘿,我刚好处理过类似的S3大文件上传场景,针对你遇到的「预签名URL不能分片」(其实是可以的!)、API Gateway/Lambda负载限制的问题,给你几个落地可行的方案:
方案1:用S3预签名URL实现分片上传
你之前可能对预签名URL的能力有误解——S3的预签名URL完全支持分片上传流程,这也是处理大文件最推荐的方案,直接绕开API Gateway和Lambda的负载限制,性能拉满。
具体流程很清晰:
- 你的Node.js后端调用
createMultipartUpload初始化分片上传,拿到唯一的UploadId - 根据图片大小拆分分片(S3要求每个分片至少5MB,最后一个分片可以更小,建议按5-10MB拆分)
- 为每个分片生成独立的预签名URL,把这些URL和
UploadId一起返回给移动端 - 移动端并行上传所有分片到对应的预签名URL(直接和S3通信,不经过你的后端)
- 所有分片上传完成后,移动端通知后端调用
completeMultipartUpload收尾,完成整个上传
给你一段Node.js的核心代码示例(用AWS SDK v3,现在官方推荐的版本):
import { S3Client, CreateMultipartUploadCommand, PutObjectCommand, CompleteMultipartUploadCommand } from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; const s3Client = new S3Client({ region: "你的AWS区域" }); // 初始化分片上传 export async function initMultipartUpload(bucketName, objectKey, contentType) { const command = new CreateMultipartUploadCommand({ Bucket: bucketName, Key: objectKey, ContentType: contentType // 比如image/jpeg、image/png }); const response = await s3Client.send(command); return { uploadId: response.UploadId, key: response.Key }; } // 生成单个分片的预签名上传URL export async function generatePresignedPartUrl(bucketName, objectKey, uploadId, partNumber) { const command = new PutObjectCommand({ Bucket: bucketName, Key: objectKey, UploadId: uploadId, PartNumber: partNumber }); // 设置URL有效期,比如1小时,足够用户完成上传 const url = await getSignedUrl(s3Client, command, { expiresIn: 3600 }); return url; } // 完成分片上传 export async function finishMultipartUpload(bucketName, objectKey, uploadId, uploadedParts) { // uploadedParts是移动端返回的每个分片的ETag和PartNumber const command = new CompleteMultipartUploadCommand({ Bucket: bucketName, Key: objectKey, UploadId: uploadId, MultipartUpload: { Parts: uploadedParts } }); await s3Client.send(command); }
这个方案完全不需要API Gateway参与,所有上传流量直接走S3,既解决了大小限制问题,又能支撑大量并发上传。
方案2:用AWS Amplify Storage省掉重复造轮子
如果不想自己写分片上传的底层逻辑,AWS Amplify的Storage模块已经封装好了S3分片上传、断点续传、权限控制等功能,不管是Node.js后端还是移动端,都能快速集成。
Node.js后端的配置示例:
import { Amplify } from 'aws-amplify'; import { Storage } from 'aws-amplify/storage'; Amplify.configure({ Auth: { // 配置你的AWS Cognito或者自定义认证方式 identityPoolId: '你的身份池ID', region: '你的AWS区域' }, Storage: { AWSS3: { bucket: '你的S3桶名', region: '你的AWS区域' } } }); // 如果你需要后端控制上传权限,可以生成带策略的上传凭证 export async function getUploadAuthorization(objectKey) { const uploadConfig = await Storage.configure(objectKey, { level: 'public', // 或者private,根据业务需求 contentType: 'image/jpeg' }); return uploadConfig; }
移动端直接调用Amplify的Storage.put方法,它会自动处理分片拆分、并行上传、失败重试,你完全不用关心底层细节,适合快速迭代的项目。
方案3:开启S3 Transfer Acceleration加速全球上传
如果你的用户分布在全球各地,可以开启S3 Transfer Acceleration,结合分片上传进一步提升上传速度。只需要在初始化S3客户端时指定加速端点即可:
const s3Client = new S3Client({ region: "你的AWS区域", endpoint: `https://${bucketName}.s3-accelerate.amazonaws.com` });
这样生成的预签名URL会指向S3的全球加速节点,减少跨地域上传的延迟,用户体验会更好。
总结一下,预签名URL分片上传是最适合你的场景的方案,既解决了API Gateway和Lambda的大小限制,又能高效处理大量图片上传;如果想省时间,Amplify Storage是绝佳的偷懒选择。
内容的提问来源于stack exchange,提问作者M14




