如何使用Boto3下载S3存储桶中的最新文件?

28

我找到的其他问题是关于较旧版本的Boto的, 我想下载S3存储桶的最新文件。在文档中,我发现有一个方法list_object_versions(),可以获取一个布尔值IsLatest。不幸的是,我只设法建立连接并下载文件。请问您能否向我展示如何扩展我的代码以获取存储桶中的最新文件?谢谢。

import boto3
conn = boto3.client('s3',
                    region_name="eu-west-1",
                    endpoint_url="customendpoint",
                    config=Config(signature_version="s3", s3={'addressing_style': 'path'}))

我不知道如何从名为mytestbucket的存储桶中获取最新添加的文件。存储桶中有各种csv文件,但它们的名称都不同。

更新:

import boto3
from botocore.client import Config

s3 = boto3.resource('s3', region_name="eu-west-1", endpoint_url="custom endpoint", aws_access_key_id = '1234', aws_secret_access_key = '1234', config=Config(signature_version="s3", s3={'addressing_style': 'path'}))
my_bucket = s3.Bucket('mytestbucket22')
unsorted = []
for file in my_bucket.objects.filter():
   unsorted.append(file)

files = [obj.key for obj in sorted(unsorted, key=get_last_modified, reverse=True)][0:9]

这给我带来了以下错误:

NameError: name 'get_last_modified' is not defined
7个回答

32

我提供的答案有所变化: Boto3 S3, sort bucket by last modified。 您可以修改代码以适应自己的需求。

get_last_modified = lambda obj: int(obj['LastModified'].strftime('%s'))

s3 = boto3.client('s3')
objs = s3.list_objects_v2(Bucket='my_bucket')['Contents']
last_added = [obj['Key'] for obj in sorted(objs, key=get_last_modified)][0]

如果您想反转排序:

[obj['Key'] for obj in sorted(objs, key=get_last_modified, reverse=True)][0]

1
谢谢。我已将配置添加到客户端并编辑了我的桶名称,但是我遇到了以下错误:get_last_modified = lambda obj: int(obj['LastModified'].strftime('%s'))。ValueError:无效的格式字符串。 - jz22
1
我正在使用3.6.1版本。 - jz22
11
如果你想获取最新添加的文件,我认为你应该将[0]修改为[-1]。请注意,这不改变原意,只是让句子更通俗易懂。 - rpanai
3
@MattBunch 是的,如果存储桶中有超过1000个对象,您需要分页、获取所有对象,然后进行排序。 - helloV
@jz22 和其他人。如果遇到“Invalid format string”错误,请将“%s”更改为“%S”(大写的'S')。请查看[这个](https://github.com/addisonlynch/pyTD/issues/1)。 - Binx
显示剩余4条评论

22

你可以做

import boto3

s3_client = boto3.client('s3')
response = s3_client.list_objects_v2(Bucket='bucket_name', Prefix='prefix')
all = response['Contents']        
latest = max(all, key=lambda x: x['LastModified'])

因此,如果您正在寻找最新的更新文件夹,您可以继续使用latest['Key'].split('/')[1] - Arnab Das
1
需要注意的是,这只会显示存储桶中前1000个对象中的最新对象。如果您的存储桶包含更多对象,则需要使用分页器。 - Anon Coward

22

这处理了S3存储桶中有超过1000个对象的情况。基本上,这是@SaadK的答案,没有for循环,并使用了更新版本的list_objects_v2。

编辑:修复了@Timothée-Jeannin发现的问题。确保识别所有页面中的最新版本。

https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Paginator.ListObjectsV2

import boto3

def get_most_recent_s3_object(bucket_name, prefix):
    s3 = boto3.client('s3')
    paginator = s3.get_paginator( "list_objects_v2" )
    page_iterator = paginator.paginate(Bucket=bucket_name, Prefix=prefix)
    latest = None
    for page in page_iterator:
        if "Contents" in page:
            latest2 = max(page['Contents'], key=lambda x: x['LastModified'])
            if latest is None or latest2['LastModified'] > latest['LastModified']:
                latest = latest2
    return latest

latest = get_most_recent_s3_object(bucket_name, prefix)

latest['Key']  # -->   'prefix/objectname'

这不是错误的吗?如果有多个页面,这将给出最后一页的最新对象,因为它可能在每次迭代时被重新定义。 - Timothée Jeannin
10
为了让下一个人不必仔细阅读/检查编辑历史记录:问题已经解决。 - Kjeld Schmidt
如何扩展此代码以下载最近的十个文件,而不仅仅是单个最近的文件? - Prithvi Boinpally
@PrithviBoinpally 在将最新的项目添加到列表之前,请先创建一个谓词并使用它。该谓词将检查当前列表是否已满,或者当前文件是否比列表中最旧的文件更新。根据性能基准测试结果,根据需要调整数据结构以实现改进。 - undefined

10
如果您有很多文件,那么您需要使用分页,正如helloV所提到的一样。这是我做的方式。
get_last_modified = lambda obj: int(obj['LastModified'].strftime('%s'))
s3 = boto3.client('s3')
paginator = s3.get_paginator( "list_objects" )
page_iterator = paginator.paginate( Bucket = "BucketName", Prefix = "Prefix")
for page in page_iterator:
    if "Contents" in page:
        last_added = [obj['Key'] for obj in sorted( page["Contents"], key=get_last_modified)][-1]

我该如何下载最新的file.here文件?它只显示文件名。请告诉我如何下载。 - user 98

3

如果你正在使用Session,那么这基本上与helloV的答案相同。

from boto3.session import Session
import settings

session = Session(aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
                          aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY)
s3 = session.resource("s3")

get_last_modified = lambda obj: int(obj.last_modified.strftime('%s'))


bckt = s3.Bucket("my_bucket")
objs = [obj for obj in bckt.objects.all()]

objs = [obj for obj in sorted(objs, key=get_last_modified)]
last_added = objs[-1].key

拥有已排序的objs可以让您快速删除除最新文件以外的所有文件。
for obj in objs[:-1]:
    s3.Object("my_bucket", obj.key).delete()

0

您应该能够使用默认的下载文件命令下载最新版本的文件

import boto3
import botocore

BUCKET_NAME = 'mytestbucket'
KEY = 'fileinbucket.txt'

s3 = boto3.resource('s3')

try:
    s3.Bucket(BUCKET_NAME).download_file(KEY, 'downloadname.txt')
except botocore.exceptions.ClientError as e:
    if e.response['Error']['Code'] == "404":
        print("The object does not exist.")
    else:
        raise

参考 链接

要获取最后修改或上传的文件,您可以使用以下方法

s3 = boto3.resource('s3')
my_bucket = s3.Bucket('myBucket')
unsorted = []
for file in my_bucket.objects.filter():
   unsorted.append(file)

files = [obj.key for obj in sorted(unsorted, key=get_last_modified, 
    reverse=True)][0:9]

正如链接中的答案所述,这并不是最优解,但它可以工作。

1
谢谢。也许我的问题没有表达清楚。我刚刚编辑了它并提供了更多信息。我想从一个包含几个CSV文件的存储桶中下载最新的文件,无论它的名称是什么,我都想下载最新的文件。 - jz22
最新的是指最近添加的文件,而不是特定文件的最新版本? - Ashan
最新添加的文件。 - jz22
谢谢。不幸的是,这给我显示了另一个错误。我已经将其放在我的问题中了。我需要导入其他东西吗? - jz22

0

我也想从S3存储桶中下载最新的文件,但是它位于特定的文件夹中。使用以下函数,通过存储桶名称和前缀(即文件夹名称)获取最新的文件名。

import boto3

def get_latest_file_name(bucket_name,prefix):
    """
    Return the latest file name in an S3 bucket folder.

    :param bucket: Name of the S3 bucket.
    :param prefix: Only fetch keys that start with this prefix (folder  name).
    """
    s3_client = boto3.client('s3')
    objs = s3_client.list_objects_v2(Bucket=bucket_name)['Contents']
    shortlisted_files = dict()            
    for obj in objs:
        key = obj['Key']
        timestamp = obj['LastModified']
        # if key starts with folder name retrieve that key
        if key.startswith(prefix):              
            # Adding a new key value pair
            shortlisted_files.update( {key : timestamp} )   
    latest_filename = max(shortlisted_files, key=shortlisted_files.get)
    return latest_filename

latest_filename = get_latest_file_name(bucket_name='use_your_bucket_name',prefix = 'folder_name/')

这段代码返回了最新的文件,但我该如何下载该文件? - user 98
很遗憾,“list_objects_v2”仅检索1000个项目(因为它进行分页),所以这段代码不起作用。 - Diogo Santiago

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接