перевірте, чи існує ключ у відрі в s3 за допомогою boto3


164

Мені хотілося б знати, чи існує ключ у boto3. Я можу зімкнути вміст відра і перевірити ключ, чи він відповідає.

Але це здається довше і надмірним. Офіційні документи Boto3 прямо вказали, як це зробити.

Можливо, я пропускаю очевидне. Хтось може вказати мені, як я можу цього досягти.

Відповіді:


195

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()і робити get()або download_file()безпосередньо, і потім обробити випадок помилки.


Дякуємо за швидку відповідь Wander. Мені просто потрібно те саме для boto3.
Prabhakar Shanmugam

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

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 рази дорожчим, ніж отримати)? Якщо ви зробите це для 100 мільйонів об'єктів, це може стати трохи дорогим ... У мене таке відчуття, що метод вилучення винятків, на жаль, найкращий поки що.
П’єр Д

21
Список може бути в 12,5 разів дорожчим за кожний запит, але один запит також може повернути 100 мільйонів об'єктів, де один отримання може повернути лише один. Тож у вашому гіпотетичному випадку було б дешевше отримати всі 100 мільйонів зі списком, а потім порівняти на місцевому рівні, ніж робити 100 мільйонів індивідуальних виїздів. Не кажучи вже про 1000x швидше, оскільки вам не знадобиться 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')
Енді Хейден

20
Я думаю, що додавання цього тесту дає вам трохи більше впевненості, що об’єкт насправді не існує, а не якась інша помилка, яка викликає виняток - зауважте, що "e" - це випадок винятку ClientError:if e.response['ResponseMetadata']['HTTPStatusCode'] == 404:
Річард

@AndyHayden Що б кожен з них намагався рахувати з точки зору Aws вартості?
петля

2
@Taylor - це запит на отримання, але без передачі даних.
Енді Хейден

1
ClientError - це спіймана кількість на 400, а не лише 404, тому вона не є надійною.
міккер

21

У Boto3, якщо ви перевіряєте чи папку (префікс), чи файл, використовуючи list_objects. Ви можете використовувати існування "Зміст" у відповіді, як перевірку на наявність об'єкта. Це ще один спосіб уникнути спроби / крім вилову, як пропонує @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"
Gunnar Cheng

3
Це повертає до 1000 об'єктів! boto3.amazonaws.com/v1/documentation/api/latest/reference/…
RoachLord

Це найефективніше рішення, оскільки це не вимагає s3:GetObjectдозволів лише s3:ListBucketдозволів
Вишрант

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; у цьому випадку проблему практично неможливо вирішити без встановлення додаткової бібліотеки.
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

Якщо в каталозі чи відрі їх менше 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.
Міккер

Але нам потрібна інформація про те, доступний файл чи ні. Це воно існує і не може бути доступним, тоді воно еквівалентно не існує. правильно?
isambitd

@mickzer перевірити зміни зараз.
isambitd

1
Щоб відповісти на ваш попередній коментар: Ні, поведінка HTTP 500 може бути спробою повторити, 401/403 виправити auth і т. Д. Важливо перевірити наявність фактичного коду помилки.
міккер

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 займає 36 млн місця на диску. Це особливо впливає, якщо ми використовуємо функції 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 повідомляє, що пітона виконуються з попередньо встановленим 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, як вимагає ОП
MarkNS

Існує дві версії бото-бібліотеки AWS. Ця відповідь не працює з версією, про яку було поставлено запитання.
MarkNS

Це точно не є правильною відповіддю для ОП, але це допомагає мені, оскільки мені потрібно використовувати boto v2. Тому я зняв негативне голосування.
haͣrͬukaͣreͤrͬu
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.