Составная загрузка объекта (multipart)
При передаче больших файлов в объектном хранилище S3 может произойти сетевой сбой, из-за которого загрузка прервется. В этом случае придется начинать передачу файла заново, что особенно неудобно при больших объемах данных.
Чтобы избежать повторной загрузки всего файла, рекомендуется использовать многосоставную (multipart) загрузку. Она позволяет разбить файл на части и загружать их по отдельности. Если произойдет ошибка — повторно загружается только та часть, которая не дошла до хранилища.
- AWS CLI
- Python API
Если AWS CLI еще не установлен, установите и настройте его. Для работы с объектным хранилищем используйте команды s3api и s3.
AWS CLI. Составная загрузка объекта (multipart)
Для начала разделите большой файл на несколько блоков фиксированного размера. Это можно сделать, например, с помощью стандартной утилиты split в Linux/macOS:
1. Разбейте файл на части
В терминале выполните команду:
split -b 100M ./backup part.
Что означает эта команда:
| Элемент | Описание |
|---|---|
| 100M | Размер каждой части — в данном случае 100 мегабайт. Можно использовать также K (килобайты) или G (гигабайты). |
| ./backup | Путь к исходному файлу, который нужно разбить. |
| part. | Префикс имен для полученных частей. Например: part.aa, part.ab, part.ac и т.д. |
2. Инициализируйте составную загрузку
В терминале выполните команду:
aws s3api create-multipart-upload \
--bucket <bucket_name> \
--key <object_name>
Укажите:
<bucket_name> — имя бакета;
<object_name> — имя объекта.
Пример ответа:
{
"Bucket": "backup-db",
"Key": "backup-logs",
"UploadId": "2~hEooQ4V1kvGrsNLmpelKhMSvnBad0nw"
}
Скопируйте значение UploadId — оно потребуется на следующих шагах.
3. Загрузите части файла
В терминале выполните команду:
aws s3api upload-part \
--bucket <bucket_name> \
--key <object_name> \
--upload-id <UploadId> \
--part-number <part_number> \
--body <path_to_part>
Укажите:
<bucket_name> — имя бакета;
<object_name> — имя объекта;
<UploadId> — идентификатор составной загрузки из пункта 2;
<part_number> — номер текущей части (от 1 до 10000);
<path_to_part> — путь к соответствующему фрагменту файла.
Пример ответа:
{
"ETag": "\"e868e0f4719e394144ef36531ee6824c\""
}
Сохраните ETag для каждой загруженной части — он нужен для последнего пункта.
Повторите команду для всех частей файла, изменяя --<part_number> и --<path_to_part>
4. Завершите составную загрузку
Когда все части загружены, объедините их в один объект.
В терминале выполните команду:
aws s3api complete-multipart-upload \
--bucket <bucket_name> \
--key <object_name> \
--upload-id <UploadId> \
--multipart-upload \
'{"Parts": [
{"PartNumber": 1, "ETag": "<etag_1>"},
{"PartNumber": 2, "ETag": "<etag_2>"},
{"PartNumber": 3, "ETag": "<etag_3>"}
]}'
Укажите:
<bucket_name> — имя бакета;
<object_name> — имя объекта;
<UploadId> — идентификатор составной загрузки из пункта 2;
<etag_n> — значение ETag из пункта 4.
Файл успешно собран из частей и доступен в объектном хранилище S3.
Подробнее об объектах см. в статье Об объектах. Рекомендуем также ознакомиться со статьей Ссылка на скачивание объекта.
Boto3 — это библиотека Python, предоставляющая программный интерфейс (SDK) для взаимодействия с объектным хранилищем S3. Подробности см. в документации AWS Boto3.
Python API. Составная загрузка объекта (multipart)
Python-скрипт, который полностью выполняет процесс multipart-загрузки большого файла в S3, включая:
- автоматическое разбиение файла на части,
- инициализацию multipart-загрузки,
- загрузку всех частей,
- завершение загрузки с использованием ETag.
import boto3
import os
import math
from pathlib import Path
# Конфигурация
bucket_name = "<bucket_name>"
object_name = "<object_name>"
file_path = "<path_to_object_name>"
part_size_mb = <part_size>
session = boto3.session.Session(profile_name='default') #Создает сессию с использованием профиля default. Профиль находится в файле ~/.aws/credentials.
s3 = session.client('s3', endpoint_url='$endpoint')
def multipart_upload(file_path, bucket_name, object_name, part_size_mb):
part_size = part_size_mb * 1024 * 1024 # байты
file_size = os.path.getsize(file_path)
total_parts = math.ceil(file_size / part_size)
print(f"File size: {file_size} bytes, Part size: {part_size} bytes, Total parts: {total_parts}")
response = s3.create_multipart_upload(Bucket=bucket_name, Key=object_name)
upload_id = response["UploadId"]
print(f"Initiated multipart upload. UploadId: {upload_id}")
parts = []
try:
with open(file_path, "rb") as f:
for part_number in range(1, total_parts + 1):
offset = (part_number - 1) * part_size
f.seek(offset)
data = f.read(part_size)
print(f"Uploading part {part_number}/{total_parts} ...")
response = s3.upload_part(
Bucket=bucket_name,
Key=object_name,
PartNumber=part_number,
UploadId=upload_id,
Body=data,
)
etag = response["ETag"]
parts.append({"PartNumber": part_number, "ETag": etag})
print(f"Uploaded part {part_number}, ETag: {etag}")
result = s3.complete_multipart_upload(
Bucket=bucket_name,
Key=object_name,
UploadId=upload_id,
MultipartUpload={"Parts": parts},
)
print("Upload complete:", result.get("Location", "(no URL)"))
except Exception as e:
print("Upload failed. Aborting multipart upload...")
s3.abort_multipart_upload(Bucket=bucket_name, Key=object_name, UploadId=upload_id)
raise e
if __name__ == "__main__":
multipart_upload(file_path, bucket_name, object_name, part_size_mb)
Укажите:
<bucket_name> — имя бакета;
<object_name> — имя объекта в хранилище;
<path_to_object_name> — путь к локальному файлу;
<part_size> — размер каждой части в МБ.
Подробнее об объектах см. в статье Об объектах. Рекомендуем также ознакомиться со статьей Ссылка на скачивание объекта.