Lambda上传对象至S3后Amplify Storage.get下载报404 NoSuchKey
返回404 NoSuchKey的核心原因是Amplify Storage的自动路径拼接规则和你Lambda中硬编码的S3对象键不匹配,和权限配置没有直接关系。
1. Amplify Storage的路径规则说明
Amplify的Storage模块不会直接使用你传入的key请求S3,会根据指定的访问级别自动拼接固定前缀,规则是固定的:
public级别(默认级别,不传level参数时默认使用):自动拼接public/前缀,实际请求的S3键为public/<你传入的key>protected级别:自动拼接protected/<当前用户Cognito身份ID>/前缀,实际请求的S3键为protected/<cognito-identity-id>/<你传入的key>private级别:自动拼接private/<当前用户Cognito身份ID>/前缀,实际请求的S3键为private/<cognito-identity-id>/<你传入的key>
你当前Lambda上传的对象键是myfolder/123.pdf,但前端调用Storage.get时默认走public级别,实际请求的是public/myfolder/123.pdf这个路径,两个路径完全对应不上,自然返回404。之前尝试传protected/private级别也失败,是因为这两个级别还会额外拼接用户ID前缀,和你上传到桶根路径下的对象完全不匹配。
2. 身份传递的认知误区
认为aws-sdk会自动把前端登录用户身份从REST端点透传到Lambda、再透传给S3的判断是错误的:
- 你当前Lambda中的S3客户端使用的是Lambda服务自身的执行角色权限,调用S3时的身份是Lambda角色,和前端登录用户没有任何关联
- 如果需要Lambda以登录用户身份操作S3,要么在API Gateway层配置Cognito授权方,将用户身份ID透传到Lambda的
event.requestContext.identity对象中,要么前端调用接口时传入Cognito临时凭证,Lambda手动初始化对应用户身份的S3客户端,不存在自动透传身份的逻辑。
3. 路径统一的落地方案
选其中一个方案执行即可,优先选第一个:
方案1:Lambda侧适配Amplify路径规则(推荐)
保持前端Storage调用逻辑不变,在Lambda上传S3时按照Amplify的规则拼接对象键即可。
如果需要前端默认(public级别)就能访问,直接给Key加public前缀:
const s3Params = { // 删掉ACL配置,Amplify创建的桶默认阻止公共ACL,权限靠桶策略和Cognito角色控制 Bucket: MY_BUCKETNAME, Key: `public/myfolder/${payload.id}.pdf`, // 加public/前缀适配默认访问级别 Body: '* MY PDF AS BUFFER *', ContentType: 'application/pdf' }
如果需要做用户级权限隔离,先从透传的event中拿到用户Cognito ID,拼接对应级别的路径:
// 前提是API Gateway已经配置Cognito授权,能正常透传用户身份 const userId = event.requestContext.identity.cognitoIdentityId const s3Params = { Bucket: MY_BUCKETNAME, // 上传到protected级别,文件所有者有读写权限,其他认证用户有读权限 Key: `protected/${userId}/myfolder/${payload.id}.pdf`, Body: '* MY PDF AS BUFFER *', ContentType: 'application/pdf' }
对应前端调用时要指定level参数,如果是读取其他用户的protected文件,还要额外传目标用户的identityId:
await Storage.get(`myfolder/${item.id}.pdf`, { download: true, level: 'protected' })
方案2:前端侧绕过Amplify自动前缀
如果不想修改Lambda上传逻辑,就放弃用Amplify Storage的封装方法,直接在前端引入aws-sdk的S3客户端,手动指定完整对象键发起请求,这个方案需要自行处理请求签名、CORS、权限校验逻辑,不推荐使用。
4. 快速调试方法
- 前端调用
Storage.get时直接打印返回的访问URL,看URL中的键参数就能知道Amplify实际请求的S3完整路径,和S3控制台中看到的对象路径一对比就能定位前缀差异 - Lambda中打印event日志时重点查看
requestContext.identity字段,确认是否正确拿到了Cognito用户ID,拿不到就说明API Gateway的授权配置有问题,没有透传身份信息 - 不要看到S3控制台存在同名文件就认为路径正确,S3的对象键是完整路径,差一个前缀就是完全独立的两个对象。
内容的提问来源于stack exchange,提问作者conor909

