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

使用预签名URL上传GZIP压缩JSON至S3的下载异常问题

问题描述

我通过后端生成预签名URL,将gzip压缩后的JSON对象上传至S3存储桶,上传流程正常(Postman手动添加content-encoding: gzip请求头后上传成功),但遇到两个问题:

  1. 在AWS控制台下载该文件时无法打开,无任何错误提示;
  2. 通过后端生成的下载预签名URL在Postman访问时,返回错误:Error: incorrect header check,请求头信息如下:
User-Agent: PostmanRuntime/7.29.2
Accept: */*
Postman-Token: d4ecd208-8474-45aa-aecf-3d41caa863f3
Host: <host>.s3.eu-west-2.amazonaws.com
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

若在AWS控制台移除文件的gzip编码标签,下载和查看功能恢复正常。

上传后端代码:

public String generateUploadLink(String bucketName, String bucketKey, int days) {

    var objectRequestBuilder = PutObjectRequest.builder().bucket(bucketName).key(bucketKey);
    objectRequestBuilder.contentType(MediaType.APPLICATION_JSON_VALUE);
    objectRequestBuilder.contentEncoding(GZIP.name().toLowerCase());

    PutObjectRequest objectRequest = objectRequestBuilder.build();
    PutObjectPresignRequest presignRequest =
        PutObjectPresignRequest.builder()
            .signatureDuration(Duration.ofDays(days))
            .putObjectRequest(objectRequest)
            .build();
    return this.s3Presigner.presignPutObject(presignRequest).url().toString();
}
问题原因

核心矛盾在于S3文件元数据与实际存储内容的不匹配:

  • 上传时设置了content-encoding: gzip元数据,该字段的作用是告知客户端「此文件已压缩,请自动解压后处理」;
  • 但实际存储的内容本身就是gzip压缩后的二进制数据,导致客户端触发双重解压逻辑:
    • AWS控制台读取元数据后自动解压,因内容已是压缩状态,解压失败导致无法打开;
    • Postman请求携带Accept-Encoding: gzip,S3返回带content-encoding: gzip的响应,Postman自动解压时发现内容格式不匹配,抛出incorrect header check错误。
解决方案

方案1:上传时不设置content-encoding元数据(推荐)

如果仅需存储压缩后的JSON,不需要客户端自动解压,可移除上传代码中的contentEncoding配置,仅保留contentType:

public String generateUploadLink(String bucketName, String bucketKey, int days) {

    var objectRequestBuilder = PutObjectRequest.builder().bucket(bucketName).key(bucketKey);
    // 仅设置内容类型,移除编码声明
    objectRequestBuilder.contentType(MediaType.APPLICATION_JSON_VALUE);

    PutObjectRequest objectRequest = objectRequestBuilder.build();
    PutObjectPresignRequest presignRequest =
        PutObjectPresignRequest.builder()
            .signatureDuration(Duration.ofDays(days))
            .putObjectRequest(objectRequest)
            .build();
    return this.s3Presigner.presignPutObject(presignRequest).url().toString();
}

上传时仍需确保请求体是gzip压缩后的JSON,Postman可保留content-encoding: gzip请求头(用于告知S3上传内容的编码格式),但S3不会将该字段作为文件元数据保存。下载时客户端将拿到原始压缩数据,可手动解压或在下载预签名URL中配置响应头实现自动解压。

方案2:下载时覆盖content-encoding元数据

若必须保留文件的content-encoding元数据,可在生成下载预签名URL时,移除响应中的编码声明,避免客户端自动解压:

public String generateDownloadLink(String bucketName, String bucketKey, int days) {
    var getObjectRequest = GetObjectRequest.builder()
            .bucket(bucketName)
            .key(bucketKey)
            // 移除响应中的content-encoding字段
            .responseContentEncoding(null)
            .build();

    var presignRequest = GetObjectPresignRequest.builder()
            .signatureDuration(Duration.ofDays(days))
            .getObjectRequest(getObjectRequest)
            .build();

    return s3Presigner.presignGetObject(presignRequest).url().toString();
}

修改后,S3返回的响应不再携带content-encoding: gzip,客户端将直接获取原始压缩文件,可手动解压查看内容。

补充说明
  • content-encoding元数据的设计目的是告知客户端文件的压缩格式,用于自动解压后处理;若文件本身已是压缩状态,不应设置该元数据;
  • 若希望客户端下载时自动解压,建议开启S3的服务器端压缩,上传未压缩的JSON,由S3自动压缩并添加正确的content-encoding元数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 20:55:20