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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 06:42:12