kiểm tra xem một khóa có tồn tại trong một thùng trong s3 bằng boto3 không


163

Tôi muốn biết nếu một khóa tồn tại trong boto3. Tôi có thể lặp nội dung xô và kiểm tra khóa nếu nó phù hợp.

Nhưng điều đó dường như lâu hơn và quá mức cần thiết. Boto3 tài liệu chính thức nêu rõ làm thế nào để làm điều này.

Có thể tôi đang thiếu điều hiển nhiên. Bất cứ ai có thể chỉ cho tôi làm thế nào tôi có thể đạt được điều này.

Câu trả lời:


194

boto.s3.key.KeyĐối tượng của Boto 2 đã từng có một existsphương thức kiểm tra xem khóa có tồn tại trên S3 hay không bằng cách thực hiện một yêu cầu CHÍNH và xem kết quả, nhưng dường như điều đó không còn tồn tại. Bạn phải làm điều đó cho chính bản thân mình:

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() thực hiện một yêu cầu CHÍNH cho một khóa duy nhất, nhanh, ngay cả khi đối tượng trong câu hỏi lớn hoặc bạn có nhiều đối tượng trong nhóm của mình.

Tất nhiên, bạn có thể kiểm tra xem đối tượng có tồn tại không vì bạn đang dự định sử dụng nó. Nếu đó là trường hợp, bạn chỉ cần quên load()và thực hiện một get()hoặc download_file()trực tiếp, sau đó xử lý trường hợp lỗi ở đó.


Cảm ơn đã trả lời nhanh chóng đi lang thang. Tôi chỉ cần tương tự cho boto3.
Bohhakar Shanmugam

12
Đối với boto3, có vẻ như điều tốt nhất bạn có thể làm lúc này là gọi head_objectđể thử và lấy siêu dữ liệu cho khóa, sau đó xử lý lỗi kết quả nếu nó không tồn tại.
Đi lang thang Nauta

1
@Leonid Bạn chắc chắn có thể, nhưng chỉ khi bạn gói nó trong một chức năng hoặc phương thức, tùy thuộc vào bạn. Tôi đã sửa đổi mã ví dụ một chút để existsboolean biến mất và rõ ràng hơn (tôi hy vọng!) Rằng mọi người phải có khả năng thích ứng điều này với tình huống của họ.
Đi lang thang Nauta

2
-1; không làm việc cho tôi. Trên boto3 phiên bản 1.5.26 tôi thấy e.response['Error']['Code']có giá trị như thế nào "NoSuchKey", không "404". Tôi đã không kiểm tra xem điều này là do sự khác biệt trong các phiên bản thư viện hay do sự thay đổi trong chính API vì câu trả lời này đã được viết. Dù bằng cách nào, trong phiên bản boto3 của tôi, cách tiếp cận ngắn hơn kiểm tra e.response['Error']['Code']là chỉ bắt s3.meta.client.exceptions.NoSuchKeyở vị trí đầu tiên.
Đánh dấu Amery

2
nếu bạn đang sử dụng s3 client(trái ngược với a resource) thì hãy s3.head_object(Bucket='my_bucket', Key='my_key')thay vìs3.Object(...).load()
user2426679

125

Tôi không phải là một fan hâm mộ lớn của việc sử dụng ngoại lệ cho luồng điều khiển. Đây là một cách tiếp cận khác hoạt động trong 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")

Cảm ơn đã cập nhật EvilPuppetMaster. Thật không may khi tôi kiểm tra lần cuối tôi đã không có quyền truy cập danh sách xô. Câu trả lời của bạn là thích hợp cho câu hỏi của tôi, vì vậy tôi đã bỏ phiếu cho bạn. Nhưng tôi đã đánh dấu câu trả lời đầu tiên là câu trả lời từ lâu trước đó. Cảm ơn bạn đã giúp đỡ.
Bohhakar Shanmugam

27
Đây không phải là một yêu cầu niêm yết (đắt hơn 12,5 lần so với nhận)? Nếu bạn làm điều này cho 100 triệu đối tượng, điều đó có thể hơi tốn kém ... Tôi có cảm giác rằng phương pháp bắt ngoại lệ không may là tốt nhất cho đến nay.
Pierre D

21
Danh sách có thể đắt 12,5 lần cho mỗi yêu cầu, nhưng một yêu cầu cũng có thể trả về 100 triệu đối tượng trong đó một lần nhận chỉ có thể trả lại một yêu cầu. Vì vậy, trong trường hợp giả thuyết của bạn, sẽ rẻ hơn khi lấy tất cả 100 triệu với danh sách và sau đó so sánh cục bộ, so với 100 triệu cá nhân nhận được. Chưa kể 1000 lần nhanh hơn vì bạn sẽ không cần chuyến đi khứ hồi http cho mọi đối tượng.
EvilPuppetMaster

Nó không hoạt động khi tệp của tôi nằm trong các thư mục trong nhóm s3
user3186866

2
@ user3186866 Đó là vì S3 không thực sự có "thư mục". Tất cả các đối tượng tồn tại dưới dạng tệp tại các đường dẫn cụ thể của chúng. Các thư mục là một công cụ giúp chúng tôi sắp xếp và hiểu cấu trúc lưu trữ của chúng tôi, nhưng trên thực tế, các thùng S3 chỉ có vậy, các thùng.
ibtokin

113

Cách dễ nhất tôi tìm thấy (và có lẽ là hiệu quả nhất) là:

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
Lưu ý: Bạn không cần phải vượt qua aws_access_key_id / aws_secret_access_key, v.v. nếu sử dụng vai trò hoặc bạn có các khóa trong bạn .aws config, bạn chỉ cần làms3 = boto3.client('s3')
Andy Hayden

20
Tôi nghĩ rằng việc thêm bài kiểm tra này giúp bạn tự tin hơn một chút, đối tượng thực sự không tồn tại, thay vì một số lỗi khác gây ra ngoại lệ - lưu ý rằng 'e' là trường hợp ngoại lệ của ClientError:if e.response['ResponseMetadata']['HTTPStatusCode'] == 404:
Richard

@AndyHayden Mỗi lần thử sẽ được tính như thế nào về chi phí aws?
vòng lặp

2
@Taylor đó là một yêu cầu nhận nhưng không truyền dữ liệu.
Andy Hayden

1
ClientError là một sản phẩm bắt được cho 400, không chỉ 404 do đó nó không mạnh.
mickzer

21

Trong Boto3, nếu bạn đang kiểm tra thư mục (tiền tố) hoặc tệp bằng list_objects. Bạn có thể sử dụng sự tồn tại của 'Nội dung' trong câu trả lời để kiểm tra xem đối tượng có tồn tại hay không. Đó là một cách khác để tránh thử / ngoại trừ sản phẩm khai thác như @EvilPuppetMaster gợi ý

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

2
Có một vấn đề trong việc này. list_objects ("2000") sẽ trả về các khóa như "2000-01", "2000-02"
Gunnar Cheng

3
Điều này chỉ trả về tối đa 1000 đối tượng! boto3.amazonaws.com/v1/documentation/api/latest/reference/ory
RoachLord 17/12/18

Đây là giải pháp hiệu quả nhất vì điều này không yêu cầu s3:GetObjectquyền chỉ có s3:ListBucketquyền
Vishrant

11

Không chỉ clientbucketcòn:

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
Bạn có thể không muốn có được đối tượng, nhưng chỉ cần xem nếu nó ở đó. Bạn có thể sử dụng một phương thức đứng đầu đối tượng như các ví dụ khác ở đây, chẳng hạn như bucket.Object(key).last_modified.
ryanjdillon

10

Bạn có thể sử dụng S3F , về cơ bản là trình bao bọc xung quanh boto3 để lộ các hoạt động kiểu hệ thống tệp điển hình:

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

Mặc dù tôi nghĩ rằng điều này sẽ làm việc, câu hỏi hỏi về cách làm điều này với boto3; trong trường hợp này, nó là thực tế để giải quyết vấn đề mà không cần cài đặt một thư viện bổ sung.
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, đây là các chức năng rất đơn giản mà tôi đang sử dụng

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
đây là phản hồi duy nhất tôi thấy có địa chỉ kiểm tra sự tồn tại của một 'thư mục' so với 'tệp'. đó là cực kỳ quan trọng đối với các thói quen cần biết nếu một thư mục cụ thể tồn tại, không phải các tệp cụ thể trong một thư mục.
dave campbell

Mặc dù đây là một câu trả lời cẩn thận nhưng nó chỉ hữu ích nếu người dùng hiểu rằng khái niệm thư mục là sai lệch trong trường hợp này. Một 'thư mục' trống có thể tồn tại trong S3 trong một nhóm và nếu vậy isdir_s3 sẽ trả về Sai, tôi mất vài phút để sắp xếp nó. Tôi đã suy nghĩ về việc chỉnh sửa câu trả lời như thể biểu thức được thay đổi thành> 0, bạn sẽ nhận được kết quả mà bạn đang mong đợi
PyNEwbie

5

Giả sử bạn chỉ muốn kiểm tra nếu một khóa tồn tại (thay vì lặng lẽ viết quá nhiều), hãy thực hiện kiểm tra này trước:

import boto3
s3_client = boto3.client('s3')
mykey = 'someprefix/myfile-abc123'
mybucket = 'my-bucket-name'
response = s3_client.list_objects_v2(Bucket=mybucket, Prefix=mykey)
if response.get('Contents'):
    print("key exists")
else:
    print("safe to put new bucket object 'mykey'")
    # 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

Hãy thử đơn giản

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

Điều này có thể kiểm tra cả tiền tố và khóa và tìm nạp tối đa 1 khóa.

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

Đối với boto3, ObjectSummary có thể được sử dụng để kiểm tra xem một đối tượng có tồn tại không.

Chứa tóm tắt về một đối tượng được lưu trữ trong nhóm Amazon S3. Đối tượng này không chứa siêu dữ liệu đầy đủ của đối tượng hoặc bất kỳ nội dung nào của đối tượng

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

Trong ObjectSummary.load

Gọi s3.Client.head_object để cập nhật các thuộc tính của tài nguyên ObjectSummary.

Điều này cho thấy rằng bạn có thể sử dụng ObjectSummarythay vì Objectnếu bạn dự định không sử dụng get(). Các load()chức năng không lấy đối tượng nó chỉ có được bản tóm tắt.


1

Đây là một giải pháp phù hợp với tôi. Một điều lưu ý là tôi biết định dạng chính xác của khóa trước thời hạn, vì vậy tôi chỉ liệt kê một tệp duy nhất

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

bạn có thể sử dụng Boto3 cho việc này.

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

Đây là đường dẫn bạn muốn kiểm tra có tồn tại hay không


Từ một %timeitthử nghiệm đơn giản, đây có vẻ là lựa chọn nhanh nhất
Itamar Katz

1

Nó thực sự đơn giản với get()phương pháp

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

Không mạnh mẽ, ngoại lệ có thể bị ném vì nhiều lý do, ví dụ HTTP 500 và mã này sẽ giả sử 404.
mickzer

Nhưng chúng tôi cần thông tin về việc tập tin có thể truy cập được hay không. Nó tồn tại và không thể truy cập được thì nó tương đương với không tồn tại. đúng?
isambitd

@mickzer kiểm tra các thay đổi ngay bây giờ.
isambitd

1
Để trả lời bạn nhận xét trước đó, Không, hành vi, trên HTTP 500 có thể là thử lại, 401/403 để sửa lỗi auth, v.v. Điều quan trọng là phải kiểm tra mã lỗi thực tế.
mickzer

0

Có một cách đơn giản để chúng tôi có thể kiểm tra xem tệp có tồn tại hay không trong nhóm S3. Chúng tôi không cần sử dụng ngoại lệ cho việc này

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

Điều này sẽ không chính xác nếu một tệp bắt đầu object_nametồn tại trong nhóm. Ví dụ my_file.txt.oldversionsẽ trả về một dương tính giả nếu bạn kiểm tra my_file.txt. Một chút trường hợp cạnh cho hầu hết, nhưng đối với một cái gì đó rộng như "tập tin tồn tại" mà bạn có khả năng sử dụng trong suốt ứng dụng của bạn có thể đáng xem xét.
Andrew Schwartz

0

Nếu bạn tìm kiếm một khóa tương đương với một thư mục thì bạn có thể muốn phương pháp này

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

Điều này hoạt động cho một khóa cha hoặc một khóa tương đương với tập tin hoặc một khóa không tồn tại. Tôi đã thử cách tiếp cận ưa thích ở trên và thất bại trên các khóa cha.


0

Tôi nhận thấy rằng chỉ để bắt ngoại lệ bằng cách sử dụng botocore.exceptions.ClientErrorchúng ta cần cài đặt botocore. botocore chiếm 36M dung lượng đĩa. Điều này đặc biệt ảnh hưởng nếu chúng ta sử dụng các chức năng lambda của aws. Thay vào đó, nếu chúng ta chỉ sử dụng ngoại lệ thì chúng ta có thể bỏ qua việc sử dụng thư viện bổ sung!

  • Tôi đang xác thực cho phần mở rộng tệp là '.csv'
  • Điều này sẽ không ném ngoại lệ nếu xô không tồn tại!
  • Điều này sẽ không ném ngoại lệ nếu xô tồn tại nhưng đối tượng không tồn tại!
  • Điều này ném ra một ngoại lệ nếu thùng rỗng!
  • Điều này ném ra một ngoại lệ nếu xô không có quyền!

Mã trông như thế này. Hãy chia sẻ suy nghĩ của bạn:

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 nói rằng thời gian chạy trăn đi kèm với boto3 được cài đặt sẵn: docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html
rinat.io

0

Chỉ cần theo chủ đề, ai đó có thể kết luận cái nào là cách hiệu quả nhất để kiểm tra xem một đối tượng có tồn tại trong S3 không?

Tôi nghĩ head_object có thể thắng vì nó chỉ kiểm tra siêu dữ liệu nhẹ hơn chính đối tượng thực tế



-1

Thủ tục thanh toán

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

Kiểm tra xem nếu một khóa cụ thể tồn tại trong thùng. Phương pháp này sử dụng một yêu cầu CHÍNH để kiểm tra sự tồn tại của khóa. Trả về: Một thể hiện của một đối tượng Key hoặc Không có

từ Tài liệu Boto S3

Bạn chỉ có thể gọi buck.get_key (tên khóa) và kiểm tra xem đối tượng được trả về là Không.


Điều này không hoạt động với boto3, theo yêu cầu của OP
MarkNS

Có hai phiên bản của thư viện boto AWS. Câu trả lời này không hoạt động với phiên bản được yêu cầu bởi câu hỏi.
MarkNS

Đó chắc chắn không phải là một câu trả lời đúng cho OP, nhưng nó giúp tôi vì tôi cần sử dụng boto v2. Đó là lý do tại sao tôi loại bỏ một phiếu bầu tiêu cực.
haͣrͬukaͣreͤrͬu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.