Boto3 S3, сортировка по последним изменениям
Мне нужно получить список элементов из S3, используя Boto3, но вместо того, чтобы возвращать порядок сортировки по умолчанию (по убыванию), я хочу, чтобы он возвращал его в обратном порядке.
Я знаю, что вы можете сделать это через awscli:
aws s3api list-objects --bucket mybucketfoo --query "reverse(sort_by(Contents,&LastModified))"
и это выполнимо через консоль пользовательского интерфейса (не уверен, если это сделано на стороне клиента или на стороне сервера)
Кажется, я не вижу, как это сделать в Boto3.
В настоящее время я извлекаю все файлы, а затем сортирую... но это кажется излишним, особенно если я забочусь о 10 или около того самых последних файлах.
Система фильтров, кажется, принимает только префикс для s3, ничего больше.
11 ответов
Я сделал небольшой вариант того, что @helloV разместил ниже. это не на 100% оптимально, но он выполняет работу с теми ограничениями, которые есть у boto3 на данный момент.
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]
Если в корзине не так много объектов, вы можете использовать Python для сортировки в соответствии с вашими потребностями.
Определите лямбду, чтобы получить время последнего изменения:
get_last_modified = lambda obj: int(obj['LastModified'].strftime('%s'))
Получить все объекты и отсортировать их по времени последнего изменения.
s3 = boto3.client('s3')
objs = s3.list_objects_v2(Bucket='my_bucket')['Contents']
[obj['Key'] for obj in sorted(objs, key=get_last_modified)]
Если вы хотите изменить сортировку:
[obj['Key'] for obj in sorted(objs, key=get_last_modified, reverse=True)]
Незначительное улучшение вышеперечисленного:
s3 = boto3.resource('s3')
my_bucket = s3.Bucket('myBucket')
files = my_bucket.objects.filter():
files = [obj.key for obj in sorted(files, key=lambda x: x.last_modified,
reverse=True)]
Сегодня можно выполнить поиск по корзине с помощью JMESPath, так же, как мы можем это сделать в AWS CLI (пример ).
import boto3
s3 = boto3.client("s3")
s3_paginator = s3.get_paginator('list_objects_v2')
s3_iterator = s3_paginator.paginate(Bucket='your-bucket-name')
filtered_iterator = s3_iterator.search(
"Contents[?starts_with(Key, 'folder6/')]"
" | reverse(sort_by(@, &to_string(LastModified)))"
" | @[].Key"
" | [:2]"
)
for key_data in filtered_iterator:
print(key_data)
Объяснение JMESPath
-
Contents[?starts_with(Key, 'folder6/')]
: необязательно, выбирает объекты внутри определенной папки. -
reverse(sort_by(@, &to_string(LastModified)))
: сортирует объекты по значению даты «LastModified» в порядке убывания. -
@[].Key
: получает имена объектов. -
[:2]
: получает первые 2.
Например, если данные сегмента выглядят так:
{
"Contents": [
{"Key": "folder6/file-64.pdf", "LastModified": "2014-11-21T19:04:05.000Z", "ETag": "\"70ee1738b6b21e2c8a43f3a5ab0eee64\"", "Size": 187932, "StorageClass": "STANDARD"},
{"Key": "folder5/file-63.pdf", "LastModified": "2014-11-21T19:03:05.000Z", "ETag": "\"70ee1738b6b21e2c8a43f3a5ab0eee63\"", "Size": 227543, "StorageClass": "STANDARD"},
{"Key": "folder6/file-62.pdf", "LastModified": "2014-11-21T19:02:05.000Z", "ETag": "\"70ee1738b6b21e2c8a43f3a5ab0eee62\"", "Size": 173484, "StorageClass": "STANDARD"},
{"Key": "folder6/file-61.pdf", "LastModified": "2014-11-21T19:01:05.000Z", "ETag": "\"70ee1738b6b21e2c8a43f3a5ab0eee61\"", "Size": 192940, "StorageClass": "STANDARD"}
]
}
Это даст такой результат:
[
"folder6/file-64.pdf",
"folder6/file-62.pdf"
]
Кажется, что нет способа сделать сортировку с помощью boto3. Согласно документации, boto3 поддерживает только эти методы для коллекций:
all(), filter(**kwargs), page_size(**kwargs), limit(**kwargs)
Надеюсь, что это поможет в некотором роде. https://boto3.readthedocs.io/en/latest/reference/services/s3.html
Более простой подход с использованием функции python3 sorted():
import boto3
s3 = boto3.resource('s3')
myBucket = s3.Bucket('name')
def obj_last_modified(myobj):
return myobj.last_modified
sortedObjects = sorted(myBucket.objects.all(), key=obj_last_modified, reverse=True)
теперь у вас есть обратный отсортированный список, отсортированный по атрибуту last_modified каждого объекта.
Чтобы получить последние измененные файлы в папке в S3:
import boto3
s3 = boto3.resource('s3')
my_bucket = s3.Bucket('bucket_name')
files = my_bucket.objects.filter(Prefix='folder_name/subfolder_name/')
files = [obj.key for obj in sorted(files, key=lambda x: x.last_modified,
reverse=True)][0:2]
print(files)
Чтобы получить два файла, которые были изменены последними:
files = [obj.key for obj in sorted(files, key=lambda x: x.last_modified,
reverse=True)][0:2]
s3 = boto3.client('s3')
get_last_modified = lambda obj: int(obj['LastModified'].strftime('%Y%m%d%H%M%S'))
def sortFindLatest(bucket_name):
resp = s3.list_objects(Bucket=bucket_name)
if 'Contents' in resp:
objs = resp['Contents']
files = sorted(objs, key=get_last_modified)
for key in files:
file = key['Key']
cx = s3.get_object(Bucket=bucket_name, Key=file)
У меня работает сортировка по дате и времени. Я использую Python3 AWS lambda. Ваш пробег может отличаться. Его можно оптимизировать, я специально сделал дискретным. Как упоминалось в предыдущем сообщении, для изменения порядка сортировки можно добавить reverse=True.
keys = []
kwargs = {'Bucket': 'my_bucket'}
while True:
resp = s3.list_objects_v2(**kwargs)
for obj in resp['Contents']:
keys.append(obj['Key'])
try:
kwargs['ContinuationToken'] = resp['NextContinuationToken']
except KeyError:
break
это даст вам все ключи в отсортированном порядке
Таким образом, мой ответ можно использовать для последнего изменения, но я подумал, что если вы пришли на эту страницу, есть вероятность, что вы захотите иметь возможность сортировать свои файлы каким-либо другим способом. Итак, чтобы убить 2 зайцев одним выстрелом:
В этой теме вы можете найти встроенный метод
sorted
. Если вы читаете документы или эту статью , вы увидите, что вы можете создать свою собственную функцию, чтобы отдать приоритет тому, как должны быть отсортированы объекты. Так например в моем случае. У меня была куча файлов, перед которыми стоял какой-то номер и, возможно, буква. Это выглядело так:
1.svg
10.svg
100a.svg
11.svg
110.svg
...
2.svg
20b.svg
200.svg
...
10011b.svg
...
etc
Я хотел, чтобы он сортировался по номеру впереди — меня не заботила буква после номера, поэтому я написал эту функцию:
def my_sort(x):
try:
# this will take the file name, split over the file type and take just the name, cast it to an int, and return it
return int(x.split(".")[0])
# if it couldn't do that
except ValueError:
# it will take the file name, split it over the extension, and take the name
n = x.split(".")[0]
s = ""
# then for each character
for e in n:
# check to see if it is a digit and append it to a string if it is
if e.isdigit():
s += e
# if its not a digit, it hit the character at the end of the name, so return it
else:
return int(s)
Это означает, что теперь я могу сделать это:
import boto3
s3r = boto3.resource('s3')
bucket = s3r.Bucket('my_bucket')
os = bucket.objects.filter(Prefix="my_prefix/")
os = [o.key.split("/")[-1] for o in os]
os = sorted(os, key=my_sort)
# do whatever with the sorted data
который будет сортировать мои файлы по числовому суффиксу в их имени.
Я могу понять ваш вариант использования. Вы можете сделать это легко с помощью команды aws s3.
Например: aws s3 ls testing1-goreplay --recursive
Дайте мне знать, если это работает для вас.