使用boto3检查s3中存储桶中是否存在密钥


164

我想知道boto3中是否存在密钥。我可以循环存储桶中的内容并检查密钥是否匹配。

但这似乎更长,并且是一个过大的杀伤力。Boto3官方文档明确说明了如何执行此操作。

可能是我缺少明显之处。谁能指出我如何实现这一目标。

Answers:


195

Boto 2的boto.s3.key.Key对象曾经有一种exists方法,该方法通过执行HEAD请求并查看结果来检查密钥是否在S3上存在,但似乎不再存在。您必须自己做:

import boto3
import botocore

s3 = boto3.resource('s3')

try:
    s3.Object('my-bucket', 'dootdoot.jpg').load()
except botocore.exceptions.ClientError as e:
    if e.response['Error']['Code'] == "404":
        # The object does not exist.
        ...
    else:
        # Something else has gone wrong.
        raise
else:
    # The object does exist.
    ...

load() 对单个键执行HEAD请求,这是快速的,即使有问题的对象很大或存储桶中有很多对象也是如此。

当然,您可能正在检查对象是否存在,因为您打算使用它。如果是这种情况,您可以只忘了load()并直接执行a get()或操作download_file(),然后在那里处理错误情况。


感谢您的快速回复Wander。我只需要boto3一样。
Prabhakar Shanmugam

12
对于boto3,您目前最好的办法是调用head_object尝试获取密钥的元数据,然后处理所产生的错误(如果不存在)。
2015年

1
@Leonid当然可以,但是只有将其包装在您可以自行决定的函数或方法中。我已经对示例代码进行了一些修改,以使exists布尔值消失了,而且,更清楚(我希望如此),人们应该根据自己的情况对此进行调整。
漫步瑙塔

2
-1; 对我不起作用。在boto3版本1.5.26中,我看到e.response['Error']['Code']的值类似"NoSuchKey",而不是"404"。自从编写此答案以来,我还没有检查这是否是由于库版本的差异或API本身的变化而引起的。无论哪种方式,在我的boto3版本中,比检查更短的方法e.response['Error']['Code']是仅s3.meta.client.exceptions.NoSuchKey首先捕获。
Mark Amery

2
如果您使用的是s3 client(而不是resource),请s3.head_object(Bucket='my_bucket', Key='my_key')改为使用s3.Object(...).load()
user2426679

126

我不是非常喜欢将异常用于控制流。这是在boto3中起作用的替代方法:

import boto3

s3 = boto3.resource('s3')
bucket = s3.Bucket('my-bucket')
key = 'dootdoot.jpg'
objs = list(bucket.objects.filter(Prefix=key))
if any([w.key == path_s3 for w in objs]):
    print("Exists!")
else:
    print("Doesn't exist")

感谢您更新EvilPuppetMaster。不幸的是,当我最后一次检查时,我没有列表存储区访问权限。您的回答很适合我的问题,所以我投票赞成您。但是我早已将第一个答复标记为答案。谢谢你的帮助。
Prabhakar Shanmugam

27
这不算作上市请求(比获得的价格贵12.5倍)吗?如果对1亿个对象执行此操作,则价格可能会有点高...我感到,捕获异常方法是迄今为止最好的方法。
皮埃尔·D

21
List的价格可能是每个请求的12.5倍,但是单个请求也可以返回1亿个对象,而单个get只能返回一个。因此,在您的假设情况下,用列表获取全部1亿个然后在本地进行比较要比1亿个单个获取便宜。更不用说提速1000倍,因为您不需要为每个对象进行http往返。
EvilPuppetMaster

当我的文件位于s3存储桶中的文件夹中时,它不起作用
user3186866

2
@ user3186866这是因为S3实际上没有“文件夹”。所有对象都以文件形式存在于它们的给定路径中。文件夹是帮助我们组织和了解存储结构的工具,但实际上,S3存储桶就是这样。
ibtokin

114

我发现(可能是最有效)的最简单方法是:

import boto3
from botocore.errorfactory import ClientError

s3 = boto3.client('s3')
try:
    s3.head_object(Bucket='bucket_name', Key='file_path')
except ClientError:
    # Not found
    pass

2
注意:您不必传递aws_access_key_id / aws_secret_access_key等。如果使用角色或您的密钥在您的.aws配置中,则只需执行s3 = boto3.client('s3')
Andy Hayden

20
我认为添加此测试可以使您更有信心该对象确实不存在,而不是其他引发异常的错误-请注意,“ e”是ClientError异常实例:if e.response['ResponseMetadata']['HTTPStatusCode'] == 404:
理查德(Richard)

@AndyHayden每次尝试都将算作AWS费用?
循环

2
@Taylor是一个get请求,但没有数据传输。
安迪·海登

1
ClientError是400的全部,而不仅仅是404,因此它不可靠。
mickzer

21

在Boto3中,如果要使用list_objects检查文件夹(前缀)或文件。您可以使用响应字典中“目录”的存在来检查对象是否存在。就像@EvilPuppetMaster建议的那样,这是避免try / except捕获的另一种方法

import boto3
client = boto3.client('s3')
results = client.list_objects(Bucket='my-bucket', Prefix='dootdoot.jpg')
return 'Contents' in results

2
在这方面有问题。list_objects( “2000”)将返回如“2000-01”, “2000-02”键
贡纳尔诚


这是最有效的解决方案,因为它不需要s3:GetObject权限,仅需要s3:ListBucket权限
Vishrant

11

不仅client但是bucket太:

import boto3
import botocore
bucket = boto3.resource('s3', region_name='eu-west-1').Bucket('my-bucket')

try:
  bucket.Object('my-file').get()
except botocore.exceptions.ClientError as ex:
  if ex.response['Error']['Code'] == 'NoSuchKey':
    print('NoSuchKey')

3
您可能不想获取该对象,而只是看它是否存在。您可以像此处的其他示例一样使用将对象作为标题的方法bucket.Object(key).last_modified
ryanjdillon '19

10

您可以使用S3Fs,它实际上是boto3的包装,它公开了典型的文件系统样式操作:

import s3fs
s3 = s3fs.S3FileSystem()
s3.exists('myfile.txt')

尽管我认为这可行,但问题是如何使用boto3做到这一点。在这种情况下,无需安装其他库就可以解决问题。
paulkernfeld

5
import boto3
client = boto3.client('s3')
s3_key = 'Your file without bucket name e.g. abc/bcd.txt'
bucket = 'your bucket name'
content = client.head_object(Bucket=bucket,Key=s3_key)
    if content.get('ResponseMetadata',None) is not None:
        print "File exists - s3://%s/%s " %(bucket,s3_key) 
    else:
        print "File does not exist - s3://%s/%s " %(bucket,s3_key)

5

FWIW,这是我正在使用的非常简单的功能

import boto3

def get_resource(config: dict={}):
    """Loads the s3 resource.

    Expects AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY to be in the environment
    or in a config dictionary.
    Looks in the environment first."""

    s3 = boto3.resource('s3',
                        aws_access_key_id=os.environ.get(
                            "AWS_ACCESS_KEY_ID", config.get("AWS_ACCESS_KEY_ID")),
                        aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY", config.get("AWS_SECRET_ACCESS_KEY")))
    return s3


def get_bucket(s3, s3_uri: str):
    """Get the bucket from the resource.
    A thin wrapper, use with caution.

    Example usage:

    >> bucket = get_bucket(get_resource(), s3_uri_prod)"""
    return s3.Bucket(s3_uri)


def isfile_s3(bucket, key: str) -> bool:
    """Returns T/F whether the file exists."""
    objs = list(bucket.objects.filter(Prefix=key))
    return len(objs) == 1 and objs[0].key == key


def isdir_s3(bucket, key: str) -> bool:
    """Returns T/F whether the directory exists."""
    objs = list(bucket.objects.filter(Prefix=key))
    return len(objs) > 1

1
这是我看到的唯一针对“文件”与“文件”相比检查存在性的响应。这对于需要知道特定文件夹(而不是文件夹中的特定文件)是否存在的例程非常重要。
戴夫·坎贝尔

尽管这是一个仔细的回答,但只有在用户了解文件夹的概念在这种情况下具有误导性的情况下,它才有用。S3中的存储桶中可以存在一个空的“文件夹”,如果是,则isdir_s3返回False,我花了几分钟时间来整理一下我正在考虑的答案,就像表达式更改为> 0一样,您将得到您所期待的结果
PyNEwbie

5

假设您只想检查密钥是否存在(而不是悄悄地覆盖它),请首先执行以下检查:

import boto3

def key_exists(mykey, mybucket):
  s3_client = boto3.client('s3')
  response = s3_client.list_objects_v2(Bucket=mybucket, Prefix=mykey)
  if response:
      for obj in response['Contents']:
          if mykey == obj['Key']:
              return True
  return False

if key_exists('someprefix/myfile-abc123', 'my-bucket-name'):
    print("key exists")
else:
    print("safe to put new bucket object")
    # try:
    #     resp = s3_client.put_object(Body="Your string or file-like object",
    #                                 Bucket=mybucket,Key=mykey)
    # ...check resp success and ClientError exception for errors...

3

试试这个简单

import boto3
s3 = boto3.resource('s3')
bucket = s3.Bucket('mybucket_name') # just Bucket name
file_name = 'A/B/filename.txt'      # full file path
obj = list(bucket.objects.filter(Prefix=file_name))
if len(obj) > 0:
    print("Exists")
else:
    print("Not Exists")

3

这可以同时检查前缀和密钥,并最多获取1个密钥。

def prefix_exits(bucket, prefix):
    s3_client = boto3.client('s3')
    res = s3_client.list_objects_v2(Bucket=bucket, Prefix=prefix, MaxKeys=1)
    return 'Contents' in res


1
S3_REGION="eu-central-1"
bucket="mybucket1"
name="objectname"

import boto3
from botocore.client import Config
client = boto3.client('s3',region_name=S3_REGION,config=Config(signature_version='s3v4'))
list = client.list_objects_v2(Bucket=bucket,Prefix=name)
for obj in list.get('Contents', []):
    if obj['Key'] == name: return True
return False

1

对于boto3,可以使用ObjectSummary检查对象是否存在。

包含存储在Amazon S3存储桶中的对象的摘要。该对象不包含该对象的完整元数据或其任何内容

import boto3
from botocore.errorfactory import ClientError
def path_exists(path, bucket_name):
    """Check to see if an object exists on S3"""
    s3 = boto3.resource('s3')
    try:
        s3.ObjectSummary(bucket_name=bucket_name, key=path).load()
    except ClientError as e:
        if e.response['Error']['Code'] == "404":
            return False
        else:
            raise e
    return True

path_exists('path/to/file.html')

ObjectSummary.load中

调用s3.Client.head_object以更新ObjectSummary资源的属性。

这表明您可以使用,ObjectSummary而不是Object如果您打算不使用get()。该load()函数不检索对象,仅获取摘要。


1

这是一个对我有用的解决方案。一个警告是我提前知道密钥的确切格式,所以我只列出单个文件

import boto3

# The s3 base class to interact with S3
class S3(object):
  def __init__(self):
    self.s3_client = boto3.client('s3')

  def check_if_object_exists(self, s3_bucket, s3_key):
    response = self.s3_client.list_objects(
      Bucket = s3_bucket,
      Prefix = s3_key
      )
    if 'ETag' in str(response):
      return True
    else:
      return False

if __name__ == '__main__':
  s3  = S3()
  if s3.check_if_object_exists(bucket, key):
    print "Found S3 object."
  else:
    print "No object found."

1

您可以为此使用Boto3。

import boto3
s3 = boto3.resource('s3')
bucket = s3.Bucket('my-bucket')
objs = list(bucket.objects.filter(Prefix=key))
if(len(objs)>0):
    print("key exists!!")
else:
    print("key doesn't exist!")

此处的关键是您要检查的路径是否存在


从简单的%timeit测试来看,这似乎是最快的选择
Itamar Katz

1

get()方法 真的很简单

import botocore
from boto3.session import Session
session = Session(aws_access_key_id='AWS_ACCESS_KEY',
                aws_secret_access_key='AWS_SECRET_ACCESS_KEY')
s3 = session.resource('s3')
bucket_s3 = s3.Bucket('bucket_name')

def not_exist(file_key):
    try:
        file_details = bucket_s3.Object(file_key).get()
        # print(file_details) # This line prints the file details
        return False
    except botocore.exceptions.ClientError as e:
        if e.response['Error']['Code'] == "NoSuchKey": # or you can check with e.reponse['HTTPStatusCode'] == '404'
            return True
        return False # For any other error it's hard to determine whether it exists or not. so based on the requirement feel free to change it to True/ False / raise Exception

print(not_exist('hello_world.txt')) 

不健壮,异常可能会被抛出的原因有很多如HTTP 500这个代码将承担404
mickzer

但是我们需要有关文件是否可访问的信息。它存在并且无法访问,那么等同于不存在。对?
isambidd

@mickzer现在检查更改。
isambitd

1
为了回复您先前的评论,不,在HTTP 500上的行为可能是重试,或者是401/403以修复auth等。检查实际的错误代码很重要。
mickzer

0

有一种简单的方法可以检查S3存储桶中的文件是否存在。我们不需要为此使用异常

sesssion = boto3.Session(aws_access_key_id, aws_secret_access_key)
s3 = session.client('s3')

object_name = 'filename'
bucket = 'bucketname'
obj_status = s3.list_objects(Bucket = bucket, Prefix = object_name)
if obj_status.get('Contents'):
    print("File exists")
else:
    print("File does not exists")

如果object_name存储桶中存在以开头的文件,这将是不正确的。例如,my_file.txt.oldversion如果您检查,将返回误报my_file.txt。对于大多数情况来说,这是一个边缘情况,但是对于诸如“文件是否存在”之类的广泛范围,您可能需要在整个应用程序中使用的情况可能值得考虑。
安德鲁·施瓦兹

0

如果您寻找与目录等效的键,则可能需要这种方法

session = boto3.session.Session()
resource = session.resource("s3")
bucket = resource.Bucket('mybucket')

key = 'dir-like-or-file-like-key'
objects = [o for o in bucket.objects.filter(Prefix=key).limit(1)]    
has_key = len(objects) > 0

这适用于父密钥或等同于file的密钥或不存在的密钥。我尝试了上面喜欢的方法,但在父键上失败了。


0

我注意到,仅仅为了捕获异常,botocore.exceptions.ClientError我们需要安装botocore。botocore占用36M的磁盘空间。如果我们使用aws lambda函数,这尤其会产生影响。代替的是,如果我们只使用异常,那么我们可以跳过使用额外的库!

  • 我正在验证文件扩展名为“ .csv”
  • 如果存储桶不存在,则不会抛出异常!
  • 如果存储桶存在但对象不存在,则不会引发异常!
  • 如果存储桶为空,则会抛出异常!
  • 如果存储桶没有权限,则会抛出异常!

代码看起来像这样。请分享您的想法:

import boto3
import traceback

def download4mS3(s3bucket, s3Path, localPath):
    s3 = boto3.resource('s3')

    print('Looking for the csv data file ending with .csv in bucket: ' + s3bucket + ' path: ' + s3Path)
    if s3Path.endswith('.csv') and s3Path != '':
        try:
            s3.Bucket(s3bucket).download_file(s3Path, localPath)
        except Exception as e:
            print(e)
            print(traceback.format_exc())
            if e.response['Error']['Code'] == "404":
                print("Downloading the file from: [", s3Path, "] failed")
                exit(12)
            else:
                raise
        print("Downloading the file from: [", s3Path, "] succeeded")
    else:
        print("csv file not found in in : [", s3Path, "]")
        exit(12)

AWS表示python运行时预装有boto3:docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html
rinat.io

0

紧随线程之后,有人可以得出结论,哪一种是检查S3中是否存在对象的最有效方法?

我认为head_object可能会获胜,因为它只是检查比实际对象本身更浅的元数据



-1

退房

bucket.get_key(
    key_name, 
    headers=None, 
    version_id=None, 
    response_headers=None, 
    validate=True
)

检查存储桶中是否存在特定密钥。此方法使用HEAD请求检查密钥是否存在。返回:Key对象的实例或None

来自Boto S3 Docs

您可以只调用bucket.get_key(keyname)并检查返回的对象是否为None。


根据OP的要求,这不适用于boto3
MarkNS

有两个版本的AWS Boto库。该答案不适用于该问题所要求的版本。
MarkNS

它肯定不是OP的正确答案,但对我有帮助,因为我需要使用boto v2。这就是为什么我取消了反对票。
haͣrͬukaͣreͤrͬu
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.