如何在boto3预签名URL分片上传中设置Content-Type请求头?
解决S3分片上传预签名URL与Fetch的SignatureDoesNotMatch问题
问题概述
用boto3实现S3分片上传时,后端通过requests调用预签名URL能正常上传,但前端用fetch上传时返回403 SignatureDoesNotMatch错误。原因是fetch会自动添加Content-Type请求头,而该头未被纳入预签名URL的签名计算逻辑中。boto3默认的generate_presigned_url方法针对upload_part操作不支持直接传入ContentType参数,修改botocore的service-2.json文件属于临时 workaround,需寻求正规实现方式。
正规解决方案:手动构造带Content-Type的预签名URL
绕过boto3generate_presigned_url对upload_part参数的限制,直接使用botocore.signers.RequestSigner手动构造签名请求,将Content-Type纳入签名计算。
修改后的代码实现
import boto3 from botocore.signers import RequestSigner BUCKET_NAME = "foo" client = boto3.client('s3') def create(key): response = client.create_multipart_upload(Bucket=BUCKET_NAME, Key=key) return response['UploadId'] def get_url(key, upload_id, chunk_number, content_type): # 初始化请求签名器 request_signer = RequestSigner( client.service_model.signing_name, client.meta.region_name, client.service_model.service_name, 'v4', client.meta.credentials, client.meta.config ) # 构造请求参数,包含Content-Type头 request_dict = { 'method': 'PUT', 'url': f'https://{BUCKET_NAME}.s3.{client.meta.region_name}.amazonaws.com/{key}', 'headers': { 'Content-Type': content_type, 'x-amz-part-number': str(chunk_number), 'x-amz-upload-id': upload_id }, 'body': '', 'context': {} } # 生成预签名URL signed_request = request_signer.generate_presigned_request( request_dict, expires_in=3600 # 有效期1小时 ) return signed_request['url'] def complete(key, upload_id, parts): client.complete_multipart_upload( Bucket=BUCKET_NAME, Key=key, UploadId=upload_id, MultipartUpload={"Parts": parts} )
前端适配
前端调用时,需确保fetch的Content-Type与后端生成预签名URL时传入的content_type一致。可通过Blob的type属性获取正确的MIME类型传递给后端:
// 假设blob是待上传的文件对象 const contentType = blob.type; // 先向后端请求带对应Content-Type的预签名URL const uploadTo = await fetch(`/get-signed-url?key=${key}&uploadId=${uploadId}&chunkNumber=1&contentType=${encodeURIComponent(contentType)}`).then(res => res.text()); // 执行上传 fetch(uploadTo, { method: 'PUT', body: blob, headers: { 'Content-Type': contentType // 显式设置确保一致,可选但更稳妥 } });
原理说明
S3预签名URL的签名逻辑会包含请求中的关键头信息,当fetch自动添加Content-Type时,若该头未在签名计算中,S3会判定签名不匹配返回403。通过手动构造签名请求并纳入Content-Type,可确保签名与实际请求头完全匹配,避免权限错误。
内容的提问来源于stack exchange,提问作者STerliakov
相关产品推荐
相关产品推荐

