You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Lambda解压S3存储桶ZIP文件时报File Error: Access Denied

错误原因定位

从错误栈看,报错是s3-unzip包调用S3 API时被权限拦截返回Access Denied,你给IAM用户绑定AdministratorAccess解决不了这个问题,核心原因按出现概率从高到低排:

  • 最高发:身份混淆。你给操作控制台/部署代码用的IAM用户加全权限,不代表Lambda运行时的身份有权限。Lambda执行S3操作默认使用绑定到函数自身的执行角色(Execution Role),这个角色没配对应权限的话,必然报拒绝访问。
  • 次高发:S3启用了SSE-KMS加密,但Lambda执行角色没有对应KMS密钥的加解密权限。KMS权限是独立于S3权限体系的,哪怕角色有S3全权限,没有KMS授权照样读不了加密对象、写不了加密后的输出文件。
  • 其他常见诱因:
    • S3桶策略配置了显式DENY规则,比如拒绝非指定VPC来源请求、强制要求特定TLS版本、限制只能用指定加密方式,Lambda默认请求不符合规则就会被拦
    • 触发Lambda的ZIP文件是其他AWS账户跨账户上传的,上传时没给桶所有者授予对象读取权限,哪怕你是桶所有者也无权读这个对象
    • 你用的s3-unzip包已经6年多没更新,内置的AWS SDK版本太老,不支持部分新的S3鉴权规则,本身就有兼容bug
排查修复步骤

1. 先把Lambda执行角色的权限配对

别再给自己的IAM用户加权限了,直接找Lambda的执行角色:

  • 打开Lambda控制台,进入函数详情页,在「配置-权限」面板找到绑定的执行角色,点链接跳转到IAM控制台
  • 给角色加最小必要权限,不要直接给管理员权限,参考策略如下,替换掉里面的桶名、KMS ARN即可:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": ["s3:GetObject", "s3:PutObject"],
            "Resource": "arn:aws:s3:::替换成你的桶名/*"
        },
        {
            "Effect": "Allow",
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::替换成你的桶名"
        },
        // 桶开了KMS加密才需要加下面这段
        {
            "Effect": "Allow",
            "Action": ["kms:Decrypt", "kms:GenerateDataKey"],
            "Resource": "替换成你的KMS密钥ARN"
        }
    ]
}

2. 排查S3侧的权限拦截规则

  • 检查桶策略的DENY条目,确认没有阻止Lambda出口IP访问、没有强制要求超出Lambda默认配置的访问条件(比如要求MFA、要求携带特定Principal标签)
  • 如果是跨账户上传的ZIP文件,联系上传方重新上传,上传时必须带上bucket-owner-full-control的ACL授权,否则你作为桶所有者也无权读取对象

3. 直接弃用s3-unzip包,换原生实现

s3-unzip最后一次更新是2018年,兼容问题极多,没必要在这个第三方包上耗时间,直接用官方AWS SDK加unzipper包写解压逻辑即可,稳定可控,参考代码:

const AWS = require('aws-sdk');
const unzipper = require('unzipper');
const s3 = new AWS.S3();

exports.handler = async (event) => {
    const bucketName = event.Records[0].s3.bucket.name;
    const fileKey = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));

    // 读取源ZIP文件流
    const zipReadStream = s3.getObject({
        Bucket: bucketName,
        Key: fileKey
    }).createReadStream();

    // 解析ZIP内容
    const zipDir = await unzipper.Parse().parse(zipReadStream);

    // 遍历解压文件上传到S3
    for await (const entry of zipDir.files) {
        if (entry.type === 'File') {
            // 这里可以自定义输出路径,比如统一放到unzipped前缀下
            const outputKey = `unzipped/${entry.path}`;
            await s3.putObject({
                Bucket: bucketName,
                Key: outputKey,
                Body: entry.stream()
            }).promise();
        }
        entry.autodrain();
    }

    return { message: 'unzip success' };
};

部署前记得在项目目录下执行npm install unzipper aws-sdk把依赖打包进部署包,Lambda运行时自带的aws-sdk版本如果太低可以手动装新版。

4. 快速排障技巧

改完配置还是报错的话,先把s3-unzip的逻辑全注释掉,在handler开头直接写一段测试代码调用s3.getObject()读目标ZIP文件:

  • 如果这段测试代码也报Access Denied,100%是IAM权限、桶策略、KMS配置的问题,和代码逻辑无关,顺着权限链路查就行
  • 如果测试代码能正常读到文件内容,就是s3-unzip包本身的问题,直接按第三步换实现即可,不用再纠结包的配置。

内容的提问来源于stack exchange,提问作者Samy15

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 02:09:19