boto3を使用して、s3のバケットにキーが存在するかどうかを確認します


165

boto3にキーが存在するかどうかを知りたい。バケットの内容をループして、一致する場合はキーを確認できます。

しかし、それは長くなり過ぎるようです。Boto3の公式ドキュメントには、これを行う方法が明記されています。

明らかなことを見逃しているかもしれません。誰か私にこれを達成する方法を教えてもらえますか?

回答:


196

Boto 2のboto.s3.key.Keyオブジェクトには、existsHEADリクエストを実行して結果を確認することにより、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()またはorをdownload_file()直接実行し、そこでエラーのケースを処理できます。


素早い返信ありがとうございます。boto3にも同じものが必要です。
Prabhakar Shanmugam

12
の場合boto3、現時点で実行できる最善の方法は、呼び出しhead_objectてキーのメタデータを取得してフェッチし、存在しない場合は結果のエラーを処理することです。
ワンダーナウタ2015年

1
@Leonid確かに可能ですが、これを関数またはメソッドでラップした場合に限ります。existsブール値がなくなるようにサンプルコードを少し変更しましたが、人々がこれを自分たちの状況に適応させることになっていることは明らかです(私は願っています!)。
Wander Nauta

2
-1; うまくいきません。boto3バージョン1.5.26 では、ではなくのe.response['Error']['Code']ような値が表示されます。これがライブラリのバージョンの違いによるものか、API自体の変更によるものかは、この回答が書かれてから確認していません。どちらの方法でも、私のバージョンのboto3では、チェックよりも短い方法は、最初にのみキャッチすることです。"NoSuchKey""404"e.response['Error']['Code']s3.meta.client.exceptions.NoSuchKey
マークアメリー2018

2
あなたはS3を使用している場合はclient(とは対照的に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を更新していただきありがとうございます。残念ながら最後に確認したところ、リストバケットへのアクセス権がありませんでした。あなたの答えは私の質問にふさわしいので、私はあなたに賛成票を投じました。しかし、私はすでにずっと前に最初の返信を回答としてマークしていました。ご協力いただきありがとうございます。
Prabhakar Shanmugam

27
これはリスティングリクエストとしてカウントされませんか(取得よりも12.5倍高くなります)?1億個のオブジェクトに対してこれを行うと、少し高価になる可能性があります...残念ながら、これまでのところ、キャッチ例外メソッドが最善の方法だと感じています。
Pierre D

21
リストはリクエストごとに12.5倍の費用がかかる可能性がありますが、1つのリクエストで1億のオブジェクトしか返せず、1つのリクエストで1億のオブジェクトを返すこともできます。したがって、架空のケースでは、1億個すべてをリストでフェッチしてからローカルで比較する方が、1億個を個別に取得するよりも安くなります。すべてのオブジェクトにhttpラウンドトリップが必要ないため、1000倍高速になることは言うまでもありません。
EvilPuppetMaster 2016年

私のファイルがs3バケット内のフォルダー内にある場合、機能しません
user3186866

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 configにキーがある場合は、aws_access_key_id / aws_secret_access_keyなどを渡す必要はありませんs3 = boto3.client('s3')
Andy Hayden

20
eはClientError例外インスタンスがあることに注意してください-私はこのテストを追加すると、あなたに少しより多くのオブジェクトが本当にむしろ例外を発生させるいくつかの他のエラーよりも、存在しない自信を与えると思いますif e.response['ResponseMetadata']['HTTPStatusCode'] == 404:
リチャード・

@AndyHayden awsコストの観点から、それぞれ何を試してみますか?
ループ

2
@Taylorこれはgetリクエストですが、データ転送はありません。
アンディヘイデン

1
ClientErrorは、404だけでなく、400のすべてをキャッチするため、堅牢ではありません。
ミッツァー

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」のようなキーを返します
Gunnar Cheng

3
これは最大1000個のオブジェクトのみを返します!boto3.amazonaws.com/v1/documentation/api/latest/reference/…– RoachLord 2018
19:42

これは必要ありませんので、これは最も効率的なソリューションであるs3:GetObjectだけで権限をs3:ListBucket権限
Vishrant

11

だけでなくclientbucketあまりにも:

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

あなたは使用することができますS3Fsを基本的に一般的なファイル・システムのスタイルのオペレーションを公開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
これは、「ファイル」と比較して「フォルダ」の存在を確認することに対処した唯一の応答です。これは、フォルダ内の特定のファイルではなく、特定のフォルダが存在するかどうかを知る必要があるルーチンにとって非常に重要です。
デイブキャンベル2018

これは慎重な答えですが、この場合、フォルダーの概念が誤解を招くものであることをユーザーが理解している場合にのみ役立ちます。空の「フォルダー」がバケット内のS3に存在する可能性があるため、isdir_s3がFalseを返す場合、それを整理するのに数分かかりました。あなたが期待している結果
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、の代わりにを使用できることを示しています。この関数は、それが唯一の概要を取得したオブジェクトを取得しません。Objectget()load()


1

ここに私のために働く解決策があります。注意点の1つは、事前にキーの正確な形式を知っているため、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

しかし、ファイルがアクセス可能かどうかについての情報が必要です。存在していてアクセスできない場合は、存在しないのと同じです。正しい?
1

@mickzerで変更を確認してください。
1

1
以前のコメントに返信するには、いいえ、HTTP 500での動作は再試行、401/403による認証の修正などです。実際のエラーコードを確認することが重要です。
ミッツァー

0

S3バケットにファイルが存在するかどうかを確認する簡単な方法が1つあります。これには例外を使用する必要はありません

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ます。ほとんどの場合、アプリケーション全体で使用する可能性のある「ファイルが存在するか」などの広範囲にわたるケースでは、考慮に値する可能性が高いです。
Andrew Schwartz

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は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がプリインストールされています: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ドキュメントから

単にbucket.get_key(keyname)を呼び出して、返されたオブジェクトがNoneかどうかを確認できます。


OPが要求したように、これは、boto3では動作しません
MarkNS

AWS botoライブラリには2つのバージョンがあります。この回答は、質問で要求されたバージョンでは機能しません。
MarkNS、2017

OPの正解で​​はないことは確かですが、boto v2を使用する必要があるため、役に立ちます。そのため、反対票を削除しました。
haͣrͬukaͣreͤrͬu
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.