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

  1. Contents[?starts_with(Key, 'folder6/')]: необязательно, выбирает объекты внутри определенной папки.
  2. reverse(sort_by(@, &to_string(LastModified))): сортирует объекты по значению даты «LastModified» в порядке убывания.
  3. @[].Key: получает имена объектов.
  4. [: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

Дайте мне знать, если это работает для вас.

Другие вопросы по тегам