JS编写AWS Lambda删除版本化S3存储桶对象未生效问题排查
问题排查结论
你的代码存在4个核心问题,导致S3删除逻辑根本没有实际执行完成:
- 异步逻辑未等待:
Array.forEach不会等待内部async回调执行,Lambda在handler函数同步代码执行完后会立即冻结运行环境,你发起的S3请求还没拿到响应就被终止,这是最核心的原因。同时你所有S3 API调用都没有加await,.promise()仅将SDK请求转为Promise对象,不await的话代码不会等待请求返回,打印的"删除成功"日志是提前输出的无效日志,实际请求结果、报错都不会被捕获。 - 无效的Promise调用:
_processRecord本身是async函数,返回值就是原生Promise,你额外调用.promise()会直接抛出静默错误,因为自定义async函数没有这个SDK专属方法。 - 版本化S3桶删除逻辑错误:对开启版本控制的S3桶,不带
VersionId调用deleteObject只会创建一个「删除标记」实现软删除,不会真正删除历史版本对象,控制台开启「显示所有版本」后就能看到原对象仍然存在;如果要彻底删除对象,必须指定版本ID删除所有对应版本。 - 事件类型错误:DynamoDB Stream触发的Lambda事件不是
APIGatewayEvent,类型定义错误容易导致字段读取异常。
修复方案
- 替换
forEach为map+Promise.all,等待所有记录的处理逻辑全部执行完成后再让handler返回,避免Lambda提前终止进程 - 所有S3异步API调用前统一加
await,确保请求执行完成后再走后续逻辑,正确捕获请求异常 - 修正版本化桶的删除逻辑:如果DynamoDB记录中存储了S3对象的版本ID,直接携带版本ID永久删除;如果未存储版本ID,先列出该Key下的所有版本(含删除标记),逐个删除所有版本
- 替换为正确的DynamoDB Stream事件类型,删除无效的
.promise()调用 - 检查Lambda执行角色权限,确保拥有
s3:HeadObject、s3:ListBucketVersions、s3:DeleteObject、s3:DeleteObjectVersion对应桶的操作权限
修复后参考代码
import { Context, DynamoDBStreamEvent } from 'aws-lambda'; const AWS = require('aws-sdk'); const s3 = new AWS.S3({ region: 'eu-west-2' }); export const handler = async (event: DynamoDBStreamEvent, context: Context) => { // 等待所有记录处理完成再返回 await Promise.all( event.Records.map(async record => { if (record.eventName === "REMOVE" && record.dynamodb?.OldImage) { await _processRecord(record.dynamodb.OldImage); } }) ); return { statusCode: 200, body: 'Process completed' }; }; async function _processRecord(oldImage: Record<string, any>) { const bucketName = process.env.BUCKETNAME; const objectKey = oldImage.propertyId.S; try { // 先确认对象存在 await s3.headObject({ Bucket: bucketName, Key: objectKey }).promise(); console.log(`Located S3 object: ${objectKey}`); // 列出该Key下所有版本(含删除标记) const versions = await s3.listObjectVersions({ Bucket: bucketName, Prefix: objectKey }).promise(); // 组装批量删除参数 const deleteParams = { Bucket: bucketName, Delete: { Objects: [ ...(versions.Versions || []).map(v => ({ Key: v.Key, VersionId: v.VersionId })), ...(versions.DeleteMarkers || []).map(v => ({ Key: v.Key, VersionId: v.VersionId })) ] } }; if (deleteParams.Delete.Objects.length > 0) { await s3.deleteObjects(deleteParams).promise(); console.log(`Successfully deleted all versions of ${objectKey}`); } } catch (error) { // 找不到对象直接跳过,其他错误打日志 if ((error as any).code === 'NotFound') { console.log(`Object ${objectKey} not found in S3, skip deletion`); return; } console.log(`Process object ${objectKey} failed:`, error); } }
注意:如果你的S3对象Key和
propertyId不是完全匹配(比如有前缀、后缀),需要调整参数里的Key值,否则会找不到对象。
内容的提问来源于stack exchange,提问作者user13155079
相关产品推荐
相关产品推荐

