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

升级AWS SDK至2.31.16后MD5值不符,求原因及适配方案

问题

我有一个使用software.amazon.awssdk依赖管理AWS存储桶内容(复制、移动、删除)的项目。以下是上传对象至存储桶的方法,其中S3Client属于software.amazon.awssdk.services.s3类,原本通过文件MD5值确保上传无损坏:

public String putS3Object(S3Client s3Client, String bucketName, String key, String md5Checksum,
                          String content) {
    PutObjectRequest metadataPutRequest = PutObjectRequest.builder()
            .bucket(bucketName)
            .key(key)
            .contentMD5(md5Checksum)
            .build();
    byte[] bytes = org.apache.commons.codec.binary.Base64.decodeBase64(content);
    PutObjectResponse putObjectResponse = s3Client.putObject(metadataPutRequest, RequestBody.fromBytes(bytes));
    return putObjectResponse.eTag();
}

MD5校验值通过以下方法计算:

public static String generateFileChecksum(Path path) {
    try (var inputStream = Files.newInputStream(path)) {
        return Base64.encodeBase64String(DigestUtils.md5(inputStream));
    } catch (IOException e) {
        throw new IllegalStateException("Failure computing checksum for file "+ path.toFile().getAbsolutePath(), e);
    }
}

此前代码运行正常,但将AWS SDK从2.21.41版本升级到2.31.16版本后,S3Client.putObject方法返回的ETag(MD5校验值)发生了变化。不清楚原因,也不知道如何重构代码适配该变化,想了解最新版本库对MD5计算的处理逻辑。

原因分析

AWS SDK for Java 2.x在2.22.x版本之后调整了默认的上传分块逻辑:

  • 旧版本(2.21.x及之前)中,当上传内容小于默认分块阈值(通常是8MB)时,会使用单块上传,此时ETag就是内容的MD5值(Base64解码后的十六进制字符串)。
  • 新版本(2.22.x及之后)默认启用了智能分块上传(将TransferManager的逻辑整合到了客户端默认行为中),即使内容小于原阈值,SDK也可能自动使用分块上传。分块上传的ETag是各分块MD5值的组合再做MD5,最后加上分块数量的后缀(比如abc123-1),和单块上传的MD5完全不同。

另外,SDK新版本对contentMD5参数的校验逻辑也有调整:如果手动传入了contentMD5,但实际上传方式是分块上传,S3服务端会忽略这个参数,因为分块上传的完整性校验是通过各分块的MD5和整体的x-amz-content-sha256来保证的。

重构方案

根据需求,有两种适配方式:

方式一:强制使用单块上传,保持ETag为原MD5格式

如果需要继续让ETag等于内容的MD5值,可以在构建PutObjectRequest时显式禁用分块上传:

public String putS3Object(S3Client s3Client, String bucketName, String key, String md5Checksum,
                          String content) {
    PutObjectRequest metadataPutRequest = PutObjectRequest.builder()
            .bucket(bucketName)
            .key(key)
            .contentMD5(md5Checksum)
            // 禁用分块上传,强制单块上传
            .disableChunkedEncoding(true)
            .build();
    byte[] bytes = org.apache.commons.codec.binary.Base64.decodeBase64(content);
    PutObjectResponse putObjectResponse = s3Client.putObject(metadataPutRequest, RequestBody.fromBytes(bytes));
    return putObjectResponse.eTag();
}

注意:这种方式仅适用于上传内容小于S3单块上传的最大限制(5GB)的场景。

方式二:适配SDK默认的分块上传逻辑,放弃ETag与MD5的绑定

如果不需要ETag保持原MD5格式,而是依赖SDK的自动分块上传优化,可以移除手动传入的contentMD5参数,转而依赖SDK自动处理的完整性校验:

public String putS3Object(S3Client s3Client, String bucketName, String key, String content) {
    PutObjectRequest metadataPutRequest = PutObjectRequest.builder()
            .bucket(bucketName)
            .key(key)
            // 移除手动传入的contentMD5,SDK会自动处理SHA-256校验
            .build();
    byte[] bytes = org.apache.commons.codec.binary.Base64.decodeBase64(content);
    PutObjectResponse putObjectResponse = s3Client.putObject(metadataPutRequest, RequestBody.fromBytes(bytes));
    return putObjectResponse.eTag();
}

此时ETag会是分块上传的组合值,但SDK会自动保证上传内容的完整性,无需手动处理MD5校验。

补充:正确计算分块上传的ETag(如果需要验证)

如果需要自己计算分块上传的ETag,可以按照以下逻辑:

  1. 将内容按SDK默认分块大小(默认8MB)拆分
  2. 计算每个分块的MD5值
  3. 将所有分块的MD5字节数组拼接在一起,再计算整体的MD5值
  4. 将整体MD5转为十六进制字符串,加上-分块数量的后缀

不过通常不需要手动计算,SDK和S3服务端会自动处理完整性校验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 11:49:59