проверить, есть ли ключ в корзине в 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.