使用Python上传图片至S3遇400错误:需采用AWS4-HMAC-SHA256授权机制
解决S3上传时的AWS4-HMAC-SHA256授权错误(替代方案)
你遇到的这个错误是因为旧版的boto(v2)默认使用AWS签名版本2,而你要上传的S3存储桶所在区域仅支持更安全的AWS4-HMAC-SHA256(签名版本4)。下面给你两种无需手动实现签名算法的解决办法:
方案1:切换到boto3(推荐)
boto3是AWS官方推荐的新版SDK,默认支持签名版本4,使用逻辑也更简洁。适配你需求的代码如下:
import boto3 from PIL import Image import io def upload_to_s3(aws_access_key_id, aws_secret_access_key, image_file, bucket, key, content_type='image/jpeg'): # 初始化S3客户端,自动适配签名版本 s3 = boto3.client( 's3', aws_access_key_id=aws_access_key_id, aws_secret_access_key=aws_secret_access_key ) # 将PIL Image对象转为字节流(满足S3上传的文件流要求) img_byte_arr = io.BytesIO() image_file.save(img_byte_arr, format='JPEG') img_byte_arr.seek(0) # 执行上传 s3.upload_fileobj( img_byte_arr, bucket, key, ExtraArgs={'ContentType': content_type} ) AWS_ACCESS_KEY = "...." AWS_ACCESS_SECRET_KEY = "....." filename = "images/image_0.jpg" file = Image.open(filename) key = "image" bucket = 'images' upload_to_s3(AWS_ACCESS_KEY, AWS_ACCESS_SECRET_KEY, file, bucket, key)
这个方案不需要你手动处理签名逻辑,boto3会根据存储桶所在区域自动选择正确的签名方式,兼容性和稳定性都更好。
方案2:修改旧boto代码以支持签名版本4
如果必须坚持使用旧版boto,可以通过指定存储桶区域并强制启用签名版本4来解决。修改你的upload_to_s3函数如下:
import io from PIL import Image import boto from boto.s3.key import Key from boto.s3.connection import S3Connection, OrdinaryCallingFormat def upload_to_s3(aws_access_key_id, aws_secret_access_key, file, bucket, key, callback=None, md5=None, reduced_redundancy=False, content_type=None): # 替换为你的存储桶实际所在区域,比如'us-east-2'、'eu-central-1' region = 'us-east-2' # 建立支持签名版本4的连接 conn = S3Connection( aws_access_key_id, aws_secret_access_key, is_secure=True, calling_format=OrdinaryCallingFormat(), region_name=region ) # 强制启用签名版本4 conn._set_auth_region_name(region) conn._auth_version = '4' bucket = conn.get_bucket(bucket, validate=False) k = Key(bucket) k.key = key # 把PIL Image转为字节流(set_contents_from_file要求传入文件流对象) img_byte_arr = io.BytesIO() file.save(img_byte_arr, format='JPEG') img_byte_arr.seek(0) k.set_contents_from_file(img_byte_arr)
注意事项:
- 必须明确存储桶所在的具体区域,替换代码中的
region值 - 旧版boto对签名版本4的支持并不完善,某些边缘场景可能仍会出现问题,因此还是优先推荐方案1
另外补充一点:你之前直接将PIL的Image对象传入set_contents_from_file是错误的,这个方法需要的是标准文件流对象,所以无论使用哪个SDK,都需要先把PIL Image转成字节流或临时文件再上传。
内容的提问来源于stack exchange,提问作者km786
相关产品推荐
相关产品推荐

