Мультипарт-загрузка в объектное хранилище Selectel: пишем тривиальный пример на Python

в 11:12, , рубрики: multipart upload, object storage, python, s3, selectel, storage, объектное хранилище
Мультипарт-загрузка в объектное хранилище Selectel: пишем тривиальный пример на Python - 1

Объектные хранилища с доступом на базе S3 API — это, возможно, лучшее решение для хранения больших объемов данных. Однако при загрузке крупных файлов могут возникнуть проблемы. Например, долгая передача данных из-за сетевых ограничений или таймауты и обрывы соединения. Как ни крути, а интернет даже здесь диктует свои условия. Попробуем их обойти с помощью мультипарт-загрузки.

Привет! Меня зовут Гришин Александр, я продакт-менеджер в Selectel и отвечаю за развитие объектного хранилища и облачных баз данных. В этой статье я расскажу, как загружать большие файлы в S3 с помощью мультипарт-загрузки, используя Python и boto3. Под катом вы узнаете, как работает этот механизм и как его настроить для эффективной работы.

Мультипарт-загрузка в объектное хранилище Selectel: пишем тривиальный пример на Python - 2Скоро выпустим новый комикс о путешествиях ИБ-специалиста! Регистрируйтесь, чтобы узнать о публикации первыми. Бонусом сможете выиграть один из 15 комплектов призов.

Используйте навигацию, если не хотите читать текст полностью:
Как работает мультипарт-загрузка в S3
Пример на Python
Оптимизация и рекомендации
А нужен ли код

Как работает мультипарт-загрузка в S3


Мультипарт-загрузка состоит из нескольких этапов.
1. Инициализация загрузки — создание уникального идентификатора загрузки.

response = s3_client.create_multipart_upload(Bucket=BUCKET_NAME, Key=KEY)
upload_id = response["UploadId"]

Этот запрос создает мультипарт-загрузку и возвращает UploadId, который используется для загрузки частей файла.
2. Разделение файла на части и их параллельная отправка.

response = s3_client.upload_part(
                Bucket=BUCKET_NAME,
                Key=KEY,
                PartNumber=part_number,
                UploadId=upload_id,
                Body=data
            )

3. Завершение загрузки и сборка частей в единый объект:

s3_client.complete_multipart_upload(
        Bucket=BUCKET_NAME,
        Key=KEY,
        UploadId=upload_id,
        MultipartUpload={"Parts": parts}
    )

4. При необходимости — отмена загрузки (например, если загрузка не удалась).

Мультипарт-загрузка в объектное хранилище Selectel: пишем тривиальный пример на Python - 3

Схема разбиения объекта на парты и их параллельная загрузка в S3.

Мультипарт-загрузка в объектное хранилище Selectel: пишем тривиальный пример на Python - 4

Пример на Python


Реализацию мультипарт-загрузки я разделю на два этапа: работа в панели управления и работа на клиенте. Итак.

Часть работы в панели управления Selectel

В целом, здесь нам нужно просто создать и настроить контейнер объектного хранилища. Очень подробный пошаговый гайд вы найдете в недавней статье моего коллеги. Я же перечислю только основные шаги:

  1. Перейдите в панель управленияОбъектное хранилище и нажмите Создать контейнер.
  2. Выберите тип адресации vHosted. Что касается типа контейнера, то для работы с чувствительными данными подойдет приватный, а если планируете реализовать доступ к контенту без авторизации, выберите публичный.
  3. Создайте служебного пользователя с ролью администратор объектного хранилища и доступом в нужный проект.
  4. Создайте S3-ключ в панели управления.
  5. Сохраните Access Key и Secret Key (будьте внимательны: ключи не хранятся в наших системах и показываются только один раз).

Мультипарт-загрузка в объектное хранилище Selectel: пишем тривиальный пример на Python - 5

Работа с сервисными пользователями в панели управления.

Часть работы на клиенте

Прежде всего, установите библиотеку boto3. Это делается командой в терминале:

pip install boto3

Теперь подключаем библиотеки и пишем код для загрузки файла в объектное хранилище, используя мультипарт-загрузку:

import boto3
import os
# Настройки подключения к Selectel S3
S3_ENDPOINT = "https://s3.ru-1.storage.selcloud.ru"  # Укажите ваш региональный endpoint
ACCESS_KEY = "ТУТ_ВАШ_ACCESS_KEY"
SECRET_KEY = "ТУТ_ВАШ_SECRET_KEY"
BUCKET_NAME = "4habr"
FILE_PATH = "/Users/alex/Downloads/1.mp4"  # Файл для загрузки
KEY = "thebigboy2.mp4"  # Название объекта в S3

# Создаем S3 клиент
s3_client = boto3.client(
    "s3",
    endpoint_url=S3_ENDPOINT,
    aws_access_key_id=ACCESS_KEY,
    aws_secret_access_key=SECRET_KEY
)

# 1. Инициализация мультипартийной загрузки
response = s3_client.create_multipart_upload(Bucket=BUCKET_NAME, Key=KEY)
upload_id = response["UploadId"]

print(f"Multipart Upload ID: {upload_id}")

# 2. Разбиение файла на части и загрузка
PART_SIZE = 1 * 1024 * 1024  # Размер одной части (1MB)
parts = []
file_size = os.path.getsize(FILE_PATH)

try:
    with open(FILE_PATH, "rb") as f:
        part_number = 1
        while True:
            data = f.read(PART_SIZE)
            if not data:
                break

            # Загружаем часть файла
            response = s3_client.upload_part(
                Bucket=BUCKET_NAME,
                Key=KEY,
                PartNumber=part_number,
                UploadId=upload_id,
                Body=data
            )

            # Добавляем информацию о части
            parts.append({"PartNumber": part_number, "ETag": response["ETag"]})
            print(f"Uploaded part {part_number}")
            part_number += 1

    # 3. Завершаем загрузку
    s3_client.complete_multipart_upload(
        Bucket=BUCKET_NAME,
        Key=KEY,
        UploadId=upload_id,
        MultipartUpload={"Parts": parts}
    )

    print("Multipart upload completed!")

except Exception as e:
    print("Upload failed:", str(e))
    s3_client.abort_multipart_upload(Bucket=BUCKET_NAME, Key=KEY, UploadId=upload_id)

Как работает этот код:

  • create_multipart_upload — создаем загрузку и получаем UploadId;
  • читаем файл частями по 1 МБ и загружаем их с помощью upload_part;
  • сохраняем ETag загруженных частей для финальной сборки;
  • complete_multipart_upload — объединяем загруженные части в единый объект;
  • если произошла ошибка, вызываем abort_multipart_upload для отмены загрузки.

Результат

Объект загружен. Можно делиться им с друзьями. Чтобы увидеть результат работы со стороны хранилища, снова зайдите в панель управления. Перейдите в нужный проект в объектном хранилище и включите отображение служебных контейнеров в настройках (это необходимо, чтобы увидеть парты загруженного объекта). В основном контейнере вы увидите ссылку на загруженный объект, а в служебном — парты этого объекта. На скриншоте ниже это шесть объектов размером до 1 МБ.

Мультипарт-загрузка в объектное хранилище Selectel: пишем тривиальный пример на Python - 6

Листинг мультипартов в интерфейсе хранилища.

Оптимизация и рекомендации


Для примера выше я установил размер парта 1 МБ, но это было сделано умышленно с целью демонстрации. Не стоит это повторять. В реальных проектах для загрузки больших файлов лучше использовать значения существенно больше, хотя бы 50-100 МБ.

Для обработки ошибок добавьте повторную попытку загрузки парта в случае сетевых проблем. А также настройте удаление ненужных частей в случае таких ошибок. В Selectel мы всегда храним все составные части загрузки, поскольку не знаем, в какой момент со стороны клиента может прийти CompleteMultipartUpload и нужны ли еще клиенту эти части.

А нужен ли код


В этой статья я использовал код только для демонстрации работы мультипартовой загрузки в объектное хранилище Selectel. Для большего удобства рекомендую использовать готовые приложения, поддерживающие такую функциональность из коробки. К ним относятся:

Пользуясь случаем, выделю именно rclone т. к. недавно мы стали официальными технологическими партнерами и получили нативную поддержку нашей услуги в этом клиенте.

Если у вас остались вопросы, смело задавайте их в комментариях, все обсудим. И поделитесь своим опытом, как вы ускоряете загрузку ваших приложений в объектное хранилище.

Автор: GrishinAlex

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js