Python 3 Boto 3,AWS S3:获取对象URL

28

我需要在上传文件后直接检索公共对象URL,以便将其存储在数据库中。 这是我的上传代码:

   s3 = boto3.resource('s3')
   s3bucket.upload_file(filepath, objectname, ExtraArgs={'StorageClass': 'STANDARD_IA'})

我不是在寻找预签名 URL,只是需要一个始终可通过 https 公开访问的 URL。

感谢您的任何帮助。

5个回答

32

没有简单的方法,但是你可以从存储桶所在的区域(get_bucket_location)、存储桶名称和存储键构造URL:

bucket_name = "my-aws-bucket"
key = "upload-file"

s3 = boto3.resource('s3')
bucket = s3.Bucket(bucket_name)
bucket.upload_file("upload.txt", key)
location = boto3.client('s3').get_bucket_location(Bucket=bucket_name)['LocationConstraint']
url = "https://s3-%s.amazonaws.com/%s/%s" % (location, bucket_name, key)

4
这个回答是错误的,你没有正确引用关键部分。同时,它也无法处理需要使用域名来访问的新存储桶。 - Antti Haapala -- Слава Україні
3
现在URL已更改为f"{bucket_name}.s3.{location}.amazonaws.com/{key}"。 - dolgom

32

自2010年以来,您可以使用基于虚拟主机的S3网址,即无需与特定区域的网址混淆:

url = f'https://{bucket}.s3.amazonaws.com/{key}'

带引号的键:

url = f'''https://{bucket}.s3.amazonaws.com/{urllib.parse.quote(key, safe="~()*!.'")}'''

此外,对于在2020年9月30日或之前创建的存储桶,将继续支持路径样式模型(区域特定URL)。在该日期之后创建的存储桶必须使用虚拟主机模型进行引用

另请参阅此博客文章


这对于GovCloud(我认为对于中国也是如此)不起作用。 - raylu
你能否再详细解释一下?(对于中国来说,我猜是amazonaws.com.cn) - lionels
这取决于 s3.amazonaws.com 端点确定您的存储桶位于哪个区域。这适用于 aws 分区,但它对 GovCloud 和中国分区一无所知。对于 GovCloud,看起来是 s3-us-gov-west-1.amazonaws.com。 - raylu
他们应该在API中实现这个功能。期望人们深入了解哪个服务器在什么时间需要哪种格式是没有意义的。 - MuhsinFatih
未正确引用键。 - Antti Haapala -- Слава Україні
如果你的键包含空格,S3会将它们替换为'+'. 更好的近似方法可能是在上述代码中用quote_plus来替换quote. - Peter Harrison

6

将原始密钥连接起来在某些特殊字符的情况下(例如:'+'),会失败,您必须对它们进行引用:

url = "https://s3-%s.amazonaws.com/%s/%s" % (
    location,
    bucket_name,
    urllib.parse.quote(key, safe="~()*!.'"),
)

您也可以拨打电话:

my_config = Config(signature_version = botocore.UNSIGNED)
url = boto3.client("s3", config=my_config).generate_presigned_url(
    "get_object", ExpiresIn=0, Params={"Bucket": bucket_name, "Key": key}
)

......如此处所述。


1
请注意,https://s3-us-east-1... 不可用,其他地区可以使用。为了获得相同的行为,您应该使用 https://s3.us-east-1...(请注意点号而不是破折号)。 请参阅 https://docs.aws.amazon.com/general/latest/gr/s3.html。 - Martin Roy

3
您可以生成一个预签名的URL,然后去掉其中的查询参数。这需要相关存储桶的"s3:PutObject"权限。
url = s3client.generate_presigned_url(ClientMethod = 'put_object',
                                      Params = { 'Bucket': bucket_name, 'Key': key })

# trim query params
url = url[0 : url.index('?')]

-2

仅是一条小注。 函数调用

location = 
    boto3.client('s3').get_bucket_location(Bucket=bucket_name['LocationConstraint']

如果存储桶位于“us-east-1”区域,则可能返回位置为None。因此,我会修改上面的答案并在该行下方添加一行:

if location == None: location = 'us-east-1'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接