Прямой ответ
Multipart-загрузка разбивает большой объект на части, которые отправляются независимо и затем собираются хранилищем. Это единственный практичный способ загрузить файл в десятки гигабайт: обычная загрузка одним запросом требует передачи всего тела заново при любом обрыве.
Протокол состоит из трёх шагов:
- Инициализация —
CreateMultipartUpload, возвращает идентификатор загрузки. - Загрузка частей —
UploadPartдля каждой части, возвращает ETag. - Завершение —
CompleteMultipartUploadсо списком пар «номер части + ETag».
import boto3
s3 = boto3.client("s3")
BUCKET, KEY = "backups", "db/2026-10-21.tar"
PART = 32 * 1024 * 1024 # 32 МБ
mp = s3.create_multipart_upload(Bucket=BUCKET, Key=KEY)
upload_id = mp["UploadId"]
parts = []
try:
with open("db.tar", "rb") as f:
for num in range(1, 10001):
data = f.read(PART)
if not data:
break
resp = s3.upload_part(Bucket=BUCKET, Key=KEY,
PartNumber=num, UploadId=upload_id, Body=data)
parts.append({"ETag": resp["ETag"], "PartNumber": num})
s3.complete_multipart_upload(Bucket=BUCKET, Key=KEY, UploadId=upload_id,
MultipartUpload={"Parts": parts})
except Exception:
s3.abort_multipart_upload(Bucket=BUCKET, Key=KEY, UploadId=upload_id)
raise
Ограничения протокола, которые нужно учитывать:
| Параметр | Значение |
|---|---|
| Число частей | От 1 до 10 000 |
| Размер части | От 5 МБ до 5 ГБ |
| Последняя часть | Может быть меньше 5 МБ |
| Максимальный объект | 5 ТБ |
Из ограничения в 10 000 частей следует практическое правило: размер части выбирают от объёма файла. Для файла 100 ГБ часть 5 МБ не подойдёт — потребуется 20 000 частей. Нужна часть не меньше 10 МБ.
Главное преимущество — повторяемость. При сбое сети повторно отправляется только неудачная часть, а не весь файл. Это критично для нестабильных каналов и для загрузки с ноутбука.
Второе преимущество — параллелизм. Части нумеруются независимо, поэтому их можно отправлять одновременно, что полностью использует доступную полосу.
Практика: надёжная multipart-загрузка
1. Используйте готовый менеджер, а не ручной цикл. Библиотеки предоставляют многопоточную загрузку с повторами:
from boto3.s3.transfer import TransferConfig
cfg = TransferConfig(
multipart_threshold=64 * 1024 * 1024,
multipart_chunksize=32 * 1024 * 1024,
max_concurrency=8,
max_retries=5,
)
s3.upload_file("db.tar", BUCKET, KEY, Config=cfg)
2. Повторяйте с экспоненциальной задержкой. Часть может не отправиться из-за временной ошибки; немедленный повтор в ту же точку часто бесполезен. Механика разобрана в статье про retry и backoff.
3. Проверяйте ETag каждой части. Номер части без корректного ETag приводит к ошибке при завершении, а ошибка на этом шаге требует загрузки всех частей заново.
4. Отменяйте незавершённые загрузки. AbortMultipartUpload удаляет уже загруженные части. Без отмены они хранятся невидимо: объекта нет в списке, а место занято и оплачивается.
5. Настройте правило очистки. Правило жизненного цикла для незавершённых multipart-загрузок удаляет их через заданное число дней автоматически:
<Rule>
<ID>abort-incomplete-uploads</ID>
<Status>Enabled</Status>
<AbortIncompleteMultipartUpload>
<DaysAfterInitiation>7</DaysAfterInitiation>
</AbortIncompleteMultipartUpload>
</Rule>
6. Не смешивайте multipart с presigned URL без планирования. Прямая загрузка из браузера тоже может быть multipart, но требует выдачи отдельной подписи на каждую часть — см. статью про presigned URL.
7. Следите за памятью. Одновременная загрузка восьми частей по 32 МБ — это 256 МБ в памяти процесса.
Рекомендации:
- Выбирайте размер части как компромисс. Мелкие части увеличивают число запросов и накладные расходы, крупные — уменьшают выгоду от повтора. Практичный диапазон 16–64 МБ.
- Логируйте идентификатор загрузки. При сбое он нужен для отмены или возобновления; без него найти незавершённую загрузку сложно.
- Проверяйте результат по размеру и контрольной сумме. Успешное завершение не гарантирует корректность содержимого.
- Ограничьте параллелизм на медленных каналах. Восемь потоков на канале 10 Мбит/с не ускоряют загрузку, но увеличивают потребление памяти.
- Проверяйте права до начала. Ошибка доступа, обнаруженная на десятой части, оставляет мусор в хранилище.
Практический сценарий: счёт за хранилище вырос, хотя объектов не прибавилось
Контекст: месячные расходы на объектное хранилище выросли на 40 % при неизменном числе объектов и объёме данных.
- Диагностика. Список объектов не изменился, но отчёт провайдера показывал рост занятого объёма.
- Проверка незавершённых загрузок. Команда перечисления multipart-загрузок показала 214 незавершённых загрузок общим объёмом около 3 ТБ. Объекты в списке не отображаются, поскольку загрузка не была завершена.
- Причина. Задача резервного копирования прерывалась по таймауту, и обработчик исключения завершал процесс без вызова
AbortMultipartUpload. Каждая неудачная попытка оставляла загруженные части. - Действия. (а) Отменили все незавершённые загрузки старше семи дней; (б) добавили правило жизненного цикла
AbortIncompleteMultipartUploadс порогом 7 дней; (в) в коде задачи отмену перенесли в блокfinally, чтобы она выполнялась при любом исключении; (г) добавили метрику на число незавершённых загрузок и алерт при превышении десяти; (д) увеличили таймаут задачи, поскольку именно он вызывал прерывания. - Результат. Занятый объём вернулся к ожидаемому; последующие сбои не оставляли мусор, а правило жизненного цикла подчищает остатки автоматически.
Вывод: незавершённые multipart-загрузки занимают место и оплачиваются, но не видны в списке объектов. Правило автоматической отмены и вызов отмены в finally — обязательная часть любой реализации загрузки частями.
FAQ
Можно ли возобновить прерванную multipart-загрузку?
Да, если известен идентификатор загрузки. Уже отправленные части перечисляются запросом ListParts, после чего догружаются только отсутствующие.
Что произойдёт, если завершить загрузку с пропущенной частью? Хранилище отклонит запрос: список частей должен быть полным и отсортированным по номерам. Объект создан не будет.
Какой минимальный размер части? 5 МБ для всех частей, кроме последней. Попытка загрузить меньшую часть приводит к ошибке при завершении загрузки.
Влияет ли multipart на итоговую контрольную сумму объекта? ETag объекта, загруженного частями, вычисляется иначе, чем для одиночной загрузки, и не совпадает с контрольной суммой файла. Для проверки целостности нужен отдельный хеш, сохранённый в метаданных.
Источники: AWS S3 docs (multipart overview, quick facts), boto3 docs — ссылки в шапке.