使用Amazon S3预签名URL上传PDF遇SignatureDoesNotMatch错误
Amazon S3预签名URL上传PDF时SignatureDoesNotMatch错误排查
问题1:是否需要在元数据中传递文件哈希?
不需要强制传递文件哈希。S3预签名URL上传默认不要求在元数据中包含文件哈希,若需验证文件完整性,可添加Content-MD5请求头,但这不是必选项,缺失该字段不会直接引发签名不匹配错误。
问题2:如何修复SignatureDoesNotMatch错误?
从错误返回的CanonicalRequest可以看出,x-amz-meta-content-type头的值为空,这是导致签名不匹配的核心原因——你将标准HTTP头Content-Type错误地放入了自定义元数据中,SDK自动为其添加了x-amz-meta-前缀,但实际请求中该头没有对应值,导致签名计算的内容与实际请求不一致。
具体修复步骤:
修正
Content-Type的设置方式Content-Type是标准HTTP头,不属于自定义元数据范畴,需通过PutObjectRequest的contentType方法单独设置,而非放入metadata参数中。修改后的代码如下:public static String createPresignedUrl(final String region, final String bucketName, final String keyName, final Map<String, String> metadata) { try (final S3Presigner presigner = S3Presigner.builder().region(Region.of(region)).build()) { // 从元数据中分离出Content-Type String contentType = metadata.remove("Content-Type"); PutObjectRequest.Builder objectRequestBuilder = PutObjectRequest.builder() .bucket(bucketName) .key(keyName) .metadata(metadata); // 若存在Content-Type则设置为标准头 if (contentType != null) { objectRequestBuilder.contentType(contentType); } PutObjectRequest objectRequest = objectRequestBuilder.build(); PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder() .signatureDuration(Duration.ofMinutes(10)) // URL有效期10分钟 .putObjectRequest(objectRequest) .build(); PresignedPutObjectRequest presignedRequest = presigner.presignPutObject(presignRequest); String myURL = presignedRequest.url().toString(); log.info("Presigned URL to upload a file to: [{}]", myURL); log.info("HTTP method: [{}]", presignedRequest.httpRequest().method()); return presignedRequest.url().toExternalForm(); } }验证请求一致性
修正后,标准的Content-Type会作为独立的签名头参与计算,不会生成空值的x-amz-meta-content-type头,确保签名计算的内容与实际请求完全匹配。额外排查点
- 确认生成预签名URL使用的AWS凭证(Access Key/Secret Key)有效,且拥有目标S3桶的
PutObject权限 - 确保上传请求使用PUT方法(与预签名URL指定的方法一致)
- 检查请求时预签名URL未过期(当前设置为10分钟)
- 禁止修改预签名URL中的任何参数(如签名、过期时间等)
- 确认生成预签名URL使用的AWS凭证(Access Key/Secret Key)有效,且拥有目标S3桶的
内容的提问来源于stack exchange,提问作者Shubham
相关产品推荐
相关产品推荐

