如何提升NestJS+Angular向Amazon S3上传50GB数据的速度?
提升Amazon S3大文件批量上传速度的优化方案
核心架构优化:跳过后端中转,前端直接传S3(优先级最高)
当前流程是Angular → 后端服务器 → S3,文件经过两次传输,严重浪费带宽和时间。改用S3预签名URL方案,让前端直接和S3交互,是提升速度最有效的方式:
- 后端新增接口:生成S3分块上传的预签名URL(包含初始化上传、上传分块、完成上传的权限)
- Angular端逻辑调整:
- 调用后端接口获取预签名信息
- 直接在前端完成分块上传到S3(无需经过后端中转)
- 所有分块上传完成后,调用后端接口写入数据库记录
- 优势:减少一次网络传输,前端可充分利用浏览器并发能力,后端仅处理鉴权和数据库操作,资源占用大幅降低
现有代码逻辑修复(若暂时无法调整架构)
1. 修复分块上传的重试逻辑
原上传函数中重试逻辑存在两处致命错误:
- 判断条件写反:
if (!failedParts.length)应该改为if (failedParts.length)(只有当有失败块时才重试) - 分块切片参数错误:重试时需按块编号正确截取文件片段
- 重试时未返回Promise,导致无法等待重试完成
修复后的关键代码片段:
// 收集失败块的partNumber(需确保uploadPart抛出错误时携带partNumber) const failedParts = parts .filter((part) => part.status === "rejected") .map((part: any) => ({ partNumber: part.reason.partNumber, reason: part.reason })); // 重试失败的块 let retriedParts = []; if (failedParts.length) { retriedParts = await Promise.all( failedParts.map((failedItem) => { const partNumber = failedItem.partNumber; return this.uploadPart( file.originalname, file.buffer.slice((partNumber - 1) * chunkSize, partNumber * chunkSize), uploadId, partNumber, bucket ); }) ); // 合并成功块与重试成功块 succeededParts = [...succeededParts, ...retriedParts]; }
2. 调整分块大小
当前分块大小设为5MB(代码中1024 * 1024 * 5),建议调整为16MB或32MB:
- S3分块上传最小块为5MB(最后一块除外),更大的块能减少API调用次数,降低网络开销
- 修改代码:
const chunkSize = 1024 * 1024 * 16; // 16MB
3. 限制后端并发处理数
原代码用Promise.all同时处理所有文件,易导致服务器内存、CPU过载,建议添加并发限制:
// 引入p-limit库实现并发控制(需先安装:npm install p-limit) import pLimit from 'p-limit'; const limit = pLimit(15); // 限制同时处理15个文件,可根据服务器配置调整 // 替换原forEach逻辑 const allPromises = photos.map(photo => limit(async () => { let file = new ChangeFileName().changeName(photo); const response = await this.s3fileUploadService.upload(file, `event-gallery-photos/${request.event_id}`); if (response.Location) { await this.eventPhotoEntity.save({ studio_id: request.studio_id, client_id: request.client_id, event_id: request.event_id, file_name: file.originalname, original_name: file.userFileName, file_size: file.size }); } })); return Promise.all(allPromises) .then(() => new ResponseFormatter(HttpStatus.OK, "Created successfully")) .catch(() => new ResponseFormatter(HttpStatus.INTERNAL_SERVER_ERROR, "Something went wrong", {}));
Angular端优化
- 并行上传而非串行:原代码循环依次调用API,改为分批次并行上传(浏览器同一域名默认并发数为6,可一次传6个,完成后再传下一批)
- 启用HTTP/2:后端配置HTTP/2,支持多路复用,提升并发请求效率
- 文件进度监控:添加上传进度回调,避免用户等待焦虑,同时可及时中断异常上传
S3配置验证
- 确认Transfer Acceleration端点正确使用:上传时必须使用S3加速域名(格式:
bucket-name.s3-accelerate.amazonaws.com),而非普通S3域名 - 开启S3智能分层:针对大文件存储,智能分层可自动优化存储性能,间接提升上传后的数据访问效率
内容的提问来源于stack exchange,提问作者chirag sorathiya
相关产品推荐
相关产品推荐

