проверить, есть ли ключ в корзине в s3, используя boto3


165

Я хотел бы знать, существует ли ключ в boto3. Я могу зациклить содержимое корзины и проверить ключ, если он совпадает.

Но это кажется дольше и излишним. Официальные документы Boto3 прямо указывают, как это сделать.

Может быть, я упускаю очевидное. Кто-нибудь может указать мне, как я могу достичь этого.

Ответы:


196

В boto.s3.key.Keyобъекте Boto 2 использовался existsметод, который проверял, существует ли ключ на S3, выполняя запрос HEAD и просматривая результат, но кажется, что его больше не существует. Вы должны сделать это самостоятельно:

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.
— Прабхакар Шанмугам

12
Поскольку boto3, кажется, лучшее, что вы можете сделать в данный момент, это позвонить, head_objectчтобы попытаться получить метаданные для ключа, а затем обработать полученную ошибку, если она не существует.
— Бродить Наута

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в первую очередь.
— Марк Амери

2
если вы используете s3 client(в отличие от a resource), то s3.head_object(Bucket='my_bucket', Key='my_key')вместоs3.Object(...).load()
— user2426679

127

Я не большой поклонник использования исключений для потока управления. Это альтернативный подход, который работает в 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. К сожалению, когда я проверял последний раз, у меня не было прав доступа к списку. Ваш ответ подходит для моего вопроса, поэтому я проголосовал за вас. Но я уже отметил первый ответ как ответ задолго до этого. Спасибо за вашу помощь.
— Прабхакар Шанмугам

27
Разве это не считается запросом листинга (в 12,5 раз дороже, чем получить)? Если вы сделаете это для 100 миллионов объектов, это может быть немного дорого ... У меня такое ощущение, что метод ловли исключений, к сожалению, пока лучший.
— Пьер Д

21
Список может быть в 12,5 раз дороже за запрос, но один запрос может также вернуть 100 миллионов объектов, при этом один запрос может вернуть только один. Таким образом, в вашем гипотетическом случае было бы дешевле собрать все 100 миллионов со списком и затем сравнить их на местном уровне, чем сделать 100 миллионов отдельных запросов. Не говоря уже о 1000-кратном ускорении, поскольку вам не понадобится http-обход для каждого объекта.
— EvilPuppetMaster

Он не работает, когда мой файл находится внутри папок внутри
— корзины

2
@ user3186866 Это потому, что S3 на самом деле не имеет «папок». Все объекты существуют в виде файлов по заданным путям. Папки - это инструмент, помогающий нам организовать и понять структуру нашего хранилища, но в действительности корзины S3 - это просто корзины.
— Ибтокин

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')
— Энди Хейден,

20
Я думаю, что добавление этого теста дает вам немного больше уверенности в том, что объект на самом деле не существует, чем какая-то другая ошибка, вызывающая исключение - обратите внимание, что 'e' - это экземпляр исключения ClientError:if e.response['ResponseMetadata']['HTTPStatusCode'] == 404:
— Ричард

@AndyHayden Что бы каждая попытка считалась с точки зрения стоимости AWS?
— петля

2
@ Тейлор это запрос на получение, но без передачи данных.
— Энди Хейден

1
ClientError - это ловушка для 400, а не только для 404, поэтому она не устойчива.
— mickzer

21

В Boto3, если вы проверяете папку (префикс) или файл, используя list_objects. Вы можете использовать существование 'Contents' в ответе dict как проверку того, существует ли объект. Это еще один способ избежать попыток за исключением попыток, как предполагает @EvilPuppetMaster.

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"
— Гуннар Ченг

3
Это возвращает только до 1000 объектов! boto3.amazonaws.com/v1/documentation/api/latest/reference/...
— RoachLord

Это наиболее эффективное решение, так как для этого не нужны 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

10

Вы можете использовать S3F , которые по сути являются оболочкой для boto3, которая предоставляет типичные операции в стиле файловой системы:

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

Хотя я думаю, что это сработает, возникает вопрос, как это сделать с помощью boto3; в этом случае целесообразно решить проблему без установки дополнительной библиотеки.
— Полкернфельд

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...

4

Это может проверить и префикс, и ключ, и выбрать не более 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

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")

1

Если у вас в каталоге или корзине меньше 1000, вы можете получить их и после проверки, если такой ключ в этом наборе:

files_in_dir = {d['Key'].split('/')[-1] for d in s3_client.list_objects_v2(
Bucket='mybucket',
Prefix='my/dir').get('Contents') or []}

Такой код работает, даже если my/dirего не существует.

http://boto3.readthedocs.io/en/latest/reference/services/s3.html#S3.Client.list_objects_v2


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теста это кажется самым быстрым вариантом
— Итамар Кац

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

Но нам нужна информация о том, доступен ли файл или нет. Если он существует и не может быть доступен, то это эквивалентно тому, что он не существует. право?
— Исамбитд

@mickzer проверьте изменения сейчас.
— Исамбитд

1
Чтобы ответить на ваш предыдущий комментарий, Нет, поведение HTTP 500 может быть повторной попыткой, 401/403 для исправления аутентификации и т. Д. Важно проверить фактический код ошибки.
— 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

Это работает для родительского ключа или ключа, который соответствует файлу, или ключа, который не существует. Я попробовал предпочтительный подход выше и потерпел неудачу на родительских ключах.


0

Я заметил, что для того, botocore.exceptions.ClientErrorчтобы перехватить исключение, нам нужно установить botocore. botocore занимает 36M дискового пространства. Это особенно важно, если мы используем лямбда-функции aws. Вместо этого, если мы просто используем исключение, мы можем пропустить дополнительную библиотеку!

  • Я проверяю, чтобы расширение файла было .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
— 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.


Это не работает с boto3, как того требует OP
— MarkNS

Есть две версии библиотеки AWS Boto. Этот ответ не работает с версией, запрошенной вопросом.
— MarkNS

Это точно не правильный ответ для OP, но это помогает мне, потому что мне нужно использовать boto v2. Вот почему я удалил отрицательный голос.
— haͣrͬukaͣreͤrͬu
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.