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

AWS Lambda Java实现S3文件下载返回API网关响应损坏问题求助

错误原因

  • 响应头配置错误:错误设置Content-Type为application/json,二进制下载响应应该设置为和文件匹配的MIME类型,不确定的情况下统一用application/octet-stream即可;同时你在响应头里加了请求头Accept,完全无效属于冗余配置,会干扰客户端解析。
  • S3流未正确关闭:读取完S3对象的字节流后没有显式关闭S3ObjectInputStream和S3Object,容易出现流截断、字节读取不全的问题,直接导致文件损坏。
  • 代码语法错误:注解@Overrider拼写错误,应为@Override;同时方法内大括号位置错误,头部逻辑结束后提前闭合了方法块,实际运行会有编译问题。
  • 冗余的JSONObject逻辑:构造的response对象完全没有被使用,属于无效代码。
  • 硬编码密钥风险:Lambda运行环境自带权限凭证链,不需要手动写AK/SK,给Lambda执行角色绑定S3只读权限即可,硬编码密钥存在严重泄露风险。
  • API Gateway配置缺失:即使代码设置了isBase64Encoded=true,也需要在API Gateway控制台的「二进制媒体类型」配置中添加需要支持的类型,比如*/*或者application/octet-stream,否则API Gateway会将Base64编码的内容当做普通字符串处理,导致内容转义损坏。
  • Content-Disposition文件名未编码:如果文件名包含中文、空格等特殊字符,会导致文件名乱码,建议用URLEncoder编码后再填入响应头。

修正后的代码

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class DownloadFileHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {

    private static final String BUCKET_NAME = "你的bucket名称";

    @Override
    public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent event, Context context) {
        String fileName = event.getQueryStringParameters().get("fileName");
        byte[] fileBytes = new byte[0];

        // 使用Lambda默认凭证链,不需要手动配置AK/SK
        AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
                .withRegion(Regions.US_EAST_2)
                .build();

        S3Object objectFile = null;
        S3ObjectInputStream objectInputStream = null;
        try {
            GetObjectRequest request = new GetObjectRequest(BUCKET_NAME, fileName);
            objectFile = s3Client.getObject(request);
            objectInputStream = objectFile.getObjectContent();
            fileBytes = objectInputStream.readAllBytes();
        } catch (IOException e) {
            e.printStackTrace();
            return new APIGatewayProxyResponseEvent()
                    .withStatusCode(500)
                    .withBody("文件读取失败");
        } finally {
            // 显式关闭流,释放资源
            if (objectInputStream != null) {
                try {
                    objectInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (objectFile != null) {
                try {
                    objectFile.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/octet-stream");
        try {
            String encodedFileName = URLEncoder.encode(fileName, "UTF-8");
            headers.put("Content-Disposition", String.format("attachment; filename=%s", encodedFileName));
        } catch (Exception e) {
            headers.put("Content-Disposition", String.format("attachment; filename=%s", fileName));
        }
        headers.put("Content-Length", String.valueOf(fileBytes.length));

        return new APIGatewayProxyResponseEvent()
                .withHeaders(headers)
                .withStatusCode(200)
                .withIsBase64Encoded(true)
                .withBody(Base64.getEncoder().encodeToString(fileBytes));
    }
}

额外注意事项

  • 部署后记得在API Gateway控制台配置二进制媒体类型,配置完成后需要重新部署API才能生效
  • 确保Lambda执行角色的IAM权限包含s3:GetObject权限,对应的资源范围包含你的S3桶路径
  • 如果文件大小超过6MB,不建议用Lambda直接返回,应该生成S3预签名链接返回给客户端,让客户端直接从S3下载,避免触发Lambda和API Gateway的 payload 大小限制

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 03:24:03