AWS Lambda流式写入S3偶现延迟,触发NoSuchKey错误
问题解决:Lambda流式解压上传S3后读取报NoSuchKey的时序问题
核心问题原因
- 未等待S3上传完成就启动读取:原代码中
uploadXMLToS3(params)是异步操作,但未等待其Promise完成;entry.pipe(pass)仅负责数据流转发,不保证S3端写入完成。Lambda在上传未持久化时就进入后续读取流程,导致S3返回NoSuchKey(后续文件会被正常写入,所以控制台能看到)。 - 错误的async/await改造方式:直接将
uploadFromStream改为async函数会返回Promise,而entry.pipe()需要接收Writable流,因此触发TypeError: dest.on is not a function。
解决方案代码修改
1. 改造uploadFromStream,同时返回流和上传Promise
让函数既提供pipe所需的流,又能让我们等待上传完成:
function uploadXMLToS3(s3XMLParams) { return S3.send(new PutObjectCommand(s3XMLParams)); } function uploadFromStream(metadata) { const pass = new stream.PassThrough(); const extension = metadata.extension; // 补全原代码中缺失的变量定义 const ContentType = getContentType(extension); const Key = `${metadata.reg}/${metadata.correlationId}.${extension}`; const params = { Bucket: metadata.bucket, Key, ContentType, Body: pass, ContentLength: metadata.size }; // 保存上传操作的Promise const uploadPromise = uploadXMLToS3(params); // 返回流和Promise供后续使用 return { stream: pass, uploadPromise }; }
2. 遍历归档文件时,等待每个文件上传完成
在for await循环中,完成数据流转发后,等待当前文件的上传Promise结束,再处理下一个文件:
const zipResponse = await S3.send(new GetObjectCommand(params)); const zip = zipResponse.Body.pipe(unzipper.Parse({ forceStream: true })); for await (const entry of zip) { // 收集元数据逻辑... const { stream, uploadPromise } = uploadFromStream(metadata); // 将解压后的文件流导向S3上传的PassThrough流 entry.pipe(stream); // 等待当前文件完全上传至S3 await uploadPromise; // 调用autodrain释放未读完的流,避免Lambda内存泄漏或超时 entry.autodrain(); } // 所有文件上传完成后,再执行后续的S3读取流程 // ...你的后续处理代码
额外优化建议
- 调整Lambda超时时间:5000个文件的大ZIP需要更长的处理时间,建议将Lambda超时设置为最大值15分钟。
- 监控上传状态:可以在
uploadPromise中添加错误捕获,避免单个文件上传失败导致整个流程中断:try { await uploadPromise; } catch (err) { console.error(`上传文件失败: ${metadata.correlationId}`, err); // 根据业务逻辑处理失败情况,比如记录日志、重试等 } - 验证S3一致性:虽然S3 PUT操作成功后GET是强一致,但如果仍有问题,可以在后续读取前通过
headObject验证文件存在后再读取。
内容的提问来源于stack exchange,提问作者Anders
相关产品推荐
相关产品推荐

