Objetivos de aprendizado
Depois de ler este artigo, você vai conseguir:
- Entender como o upload multipart divide, envia e junta um arquivo
- Enviar um arquivo grande e retomar um envio interrompido com Python
- Evitar os erros comuns: partes pequenas demais, ETag perdido e partes órfãs
Nesta página
- Como funciona um upload multipart?
- Quais são os limites do multipart no S3?
- Como enviar um arquivo grande em Python?
- Como retomar um upload interrompido?
- Como enviar do navegador sem passar pelo seu servidor?
- Quando usar o protocolo tus?
- Quais erros evitar?
- Como fazer isso na Square Cloud
- Perguntas frequentes
Enviar um vídeo de 4 GB em uma única requisição HTTP funciona até o Wi-Fi oscilar aos 97%. Aí o envio cai e você recomeça do zero. Um servidor que recebe esse arquivo também precisa manter uma conexão aberta por muito tempo, com timeouts de proxy e limites de corpo de requisição no caminho.
O upload multipart resolve isso na raiz: o arquivo vira várias partes menores, cada uma com sua própria requisição. Os números abaixo são do Amazon S3, que define o padrão seguido por quase todo storage compatível com S3; confira os limites atuais na documentação oficial.
Como funciona um upload multipart?#
A documentação do S3 descreve três etapas:
- Iniciar (
CreateMultipartUpload). O storage devolve umUploadId, que identifica esse envio em todas as chamadas seguintes. Os metadados do objeto vão nesta chamada. - Enviar as partes (
UploadPart). Cada parte tem um número de 1 a 10.000. O storage responde com umETagpara cada uma. Você pode enviar em qualquer ordem e em paralelo. - Concluir (
CompleteMultipartUpload). Você manda a lista de números de parte e seus ETags. O storage junta as partes na ordem dos números e cria o objeto.
Enviar de novo uma parte com o mesmo número sobrescreve a anterior. É assim que você reenvia uma parte que falhou sem tocar nas outras.
Quais são os limites do multipart no S3?#
| Item | Limite no Amazon S3 |
|---|---|
| Tamanho máximo do objeto | 48,8 TiB |
| Número de partes | 1 a 10.000 |
| Tamanho de cada parte | 5 MiB a 5 GiB (a última pode ser menor) |
Disso saem duas contas. Com no máximo 10.000 partes, um arquivo de 100 GB exige partes de pelo menos 10 MB. E partes de 5 MiB limitam o arquivo a cerca de 48,8 GiB (10.000 × 5 MiB). Calcule o tamanho da parte como tamanho_do_arquivo / 10.000, arredondado para cima, e nunca abaixo de 5 MiB.
Outros storages compatíveis com S3 podem ter limites diferentes. Confira a documentação do seu fornecedor antes de fixar o tamanho da parte.
Como enviar um arquivo grande em Python?#
O caminho mais curto é o upload_file do boto3, que decide sozinho quando usar multipart e envia as partes em threads. Instale com pip install boto3.
import os
import boto3
from boto3.s3.transfer import TransferConfig
s3 = boto3.client(
"s3",
endpoint_url=os.environ.get("S3_ENDPOINT"), # omita no AWS S3
aws_access_key_id=os.environ["S3_ACCESS_KEY"],
aws_secret_access_key=os.environ["S3_SECRET_KEY"],
)
config = TransferConfig(
multipart_threshold=64 * 1024 * 1024, # padrão: 8 MiB
multipart_chunksize=64 * 1024 * 1024, # padrão: 8 MiB por parte
max_concurrency=10, # padrão: 10 threads
)
s3.upload_file("video.mp4", "meu-bucket", "videos/video.mp4", Config=config)Os padrões do TransferConfig são 8 MiB para limite e tamanho de parte, e 10 threads. Se a conexão for estável, partes maiores reduzem o número de requisições. Se for instável, partes menores perdem menos a cada falha.
Para verificar, rode aws s3 ls s3://meu-bucket/videos/ --human-readable (com --endpoint-url em outro storage) e compare o tamanho com o do arquivo local.
Como retomar um upload interrompido?#
O upload_file não retoma sozinho: se o processo morre, as partes já enviadas ficam no storage, mas o próximo upload_file começa um envio novo. Para retomar, use as chamadas de baixo nível e grave o upload ID e o ETag de cada parte num arquivo local, à medida que as partes são aceitas.
import json
import os
import boto3
s3 = boto3.client(
"s3",
endpoint_url=os.environ.get("S3_ENDPOINT"),
aws_access_key_id=os.environ["S3_ACCESS_KEY"],
aws_secret_access_key=os.environ["S3_SECRET_KEY"],
)
BUCKET, KEY, PATH = "meu-bucket", "videos/video.mp4", "video.mp4"
PART = 64 * 1024 * 1024 # 64 MiB: cobre arquivos de até ~625 GiB em 10.000 partes
STATE = PATH + ".upload.json" # upload ID e ETags, salvos a cada parte
if os.path.exists(STATE):
with open(STATE) as f:
state = json.load(f)
else:
upload_id = s3.create_multipart_upload(Bucket=BUCKET, Key=KEY)["UploadId"]
state = {"upload_id": upload_id, "parts": {}}
def save():
with open(STATE, "w") as f:
json.dump(state, f)
save()
done = state["parts"] # número da parte (texto no JSON) -> ETag
with open(PATH, "rb") as f:
number = 1
while chunk := f.read(PART):
if str(number) not in done:
r = s3.upload_part(
Bucket=BUCKET, Key=KEY, UploadId=state["upload_id"],
PartNumber=number, Body=chunk,
)
done[str(number)] = r["ETag"]
save()
print("parte", number, "enviada")
number += 1
s3.complete_multipart_upload(
Bucket=BUCKET, Key=KEY, UploadId=state["upload_id"],
MultipartUpload={
"Parts": [
{"PartNumber": int(n), "ETag": e}
for n, e in sorted(done.items(), key=lambda item: int(item[0]))
]
},
)
os.remove(STATE)
print("concluído")Se o script cair no meio, rode de novo: ele lê o arquivo de estado, pula as partes já registradas e envia só as que faltam. A documentação do S3 pede para concluir com a sua própria lista de números e ETags; o list_parts serve só para conferir o que chegou ao storage. O PART precisa ser o mesmo nas duas execuções, senão as partes antigas não encaixam. O código lê uma parte por vez, então a memória usada é a de uma parte, não a do arquivo.
O exemplo envia as partes em sequência para o código ficar legível. Para paralelismo, use o upload_file do exemplo anterior ou um ThreadPoolExecutor chamando upload_part.
Como enviar do navegador sem passar pelo seu servidor?#
O padrão é o servidor assinar uma URL assinada por parte, e o navegador enviar cada parte direto ao storage. No servidor, depois do create_multipart_upload:
url = s3.generate_presigned_url(
"upload_part",
Params={"Bucket": BUCKET, "Key": KEY, "UploadId": upload_id, "PartNumber": 1},
ExpiresIn=3600,
)No navegador, fatie o arquivo com file.slice(inicio, fim) e envie cada fatia com fetch(url, { method: "PUT", body: fatia }). A resposta traz o ETag no cabeçalho ETag. Para o JavaScript conseguir lê-lo, a configuração de CORS do bucket precisa expor esse cabeçalho (ExposeHeaders). Ao final, o navegador devolve a lista de ETags ao seu servidor, que chama complete_multipart_upload.
Quando usar o protocolo tus?#
O multipart do S3 retoma porque o storage guarda as partes. O tus é outro caminho: um protocolo aberto sobre HTTP (versão 1.0.0) em que o servidor lembra quantos bytes recebeu. O cliente pergunta com HEAD e continua com PATCH:
HEAD /files/24e533e02ec3bc40c387f1a0e460e216 HTTP/1.1
Tus-Resumable: 1.0.0
HTTP/1.1 200 OK
Upload-Offset: 70
Upload-Length: 100
Cache-Control: no-store
Tus-Resumable: 1.0.0PATCH /files/24e533e02ec3bc40c387f1a0e460e216 HTTP/1.1
Tus-Resumable: 1.0.0
Upload-Offset: 70
Content-Type: application/offset+octet-stream
Content-Length: 30
[30 bytes restantes]Se o Upload-Offset enviado não bater com o do servidor, ele responde 409 Conflict. Escolha tus quando você já opera um servidor de uploads e quer retomar a partir de qualquer cliente. Para enviar direto ao storage, o multipart com URLs assinadas dispensa esse servidor.
Quais erros evitar?#
- Partes menores que 5 MiB. No S3, toda parte, exceto a última, precisa ter pelo menos 5 MiB.
- Perder o ETag ou o número da parte. O
completeexige a lista completa. Guarde cada par assim que a parte for aceita. - Mudar o tamanho da parte ao retomar. As partes antigas deixam de bater com as novas.
- Deixar uploads incompletos. No S3, as partes ficam guardadas e cobradas até o upload ser concluído ou abortado. Chame
abort_multipart_uploadao desistir e crie uma regra de ciclo de vida comAbortIncompleteMultipartUpload(por exemplo,DaysAfterInitiation: 7). Veja também regras de ciclo de vida. - Esperar que o ETag seja o MD5 do arquivo. Em objetos multipart, o ETag não é necessariamente o MD5 dos dados. Para conferir integridade, use os checksums do S3.
- Usar multipart para arquivos pequenos. Cada parte é uma requisição a mais. Para arquivos pequenos, um PUT simples resolve com uma requisição só.
Como fazer isso na Square Cloud#
No Blob Storage da Square Cloud, o vídeo de 4 GB do início cabe com folga: cada objeto pode ter até 10 GB. O multipart está disponível e só é obrigatório acima de 100 MB por PUT; abaixo disso, um PUT simples resolve. Como a API é compatível com S3, os dois scripts deste artigo rodam sem mudar a lógica: defina S3_ENDPOINT=https://s3-blob.squarecloud.app, AWS_DEFAULT_REGION=auto e as chaves S3 que você gera na aba "Acesso S3" do painel, e troque meu-bucket por public ou private, os buckets fixos da conta em que você grava.
O multipart_threshold de 64 MiB do exemplo já fica abaixo dos 100 MB, então todo arquivo que exige multipart segue por ele, e o script de retomada usa as mesmas chamadas de multipart que o Blob aceita. Os limites de parte da tabela são os do Amazon S3; no Blob, o que muda é o teto de 10 GB do objeto final. Para arquivos de até 100 MB enviados do navegador, uma única URL pré-assinada de PUT basta: ela vale por até 7 dias, e o CORS do Blob aceita PUT de qualquer origem.
Não há taxa de entrada nem de saída, e cada plano inclui uma cota de 5 GB a 250 GB. Gere as chaves no painel e rode o script de retomada com um arquivo seu, interrompendo no meio para ver as partes já enviadas sendo puladas. Conheça o Blob Storage e veja a cota de cada plano em Preços.
Blob Storage
Object storage compatível com S3, sem taxa de saída
Perguntas frequentes
A partir de que tamanho vale usar upload multipart?
A documentação do Amazon S3 recomenda considerar multipart a partir de 100 MB. O boto3 já passa a usar multipart sozinho acima de 8 MiB por padrão. Em conexões instáveis, vale usar antes disso.
Qual é o tamanho máximo de cada parte no S3?
No Amazon S3, cada parte tem de 5 MiB a 5 GiB, exceto a última, que pode ser menor. Um upload aceita até 10.000 partes e o objeto final pode chegar a 48,8 TiB. Storages compatíveis com S3 podem ter limites próprios.
Multipart e upload retomável são a mesma coisa?
Não. Multipart é como o S3 divide um objeto em partes. Retomável é poder continuar de onde parou, e o multipart permite isso porque as partes já enviadas ficam guardadas. O protocolo tus chega ao mesmo resultado por outro caminho, com HEAD e PATCH.
O que acontece com as partes de um upload que nunca terminou?
No Amazon S3, as partes ficam guardadas e são cobradas até você concluir ou abortar o upload. Configure uma regra de ciclo de vida com AbortIncompleteMultipartUpload para limpá-las.
Mais em Storage
Ver todos os 15 artigos- O que é uma URL assinada (pre-signed URL) e quando usarUma URL assinada (pre-signed URL) é um link temporário para um arquivo privado. Veja como funciona, o que ela não faz e quando usar.
- O que é armazenamento de objetos (object storage)Armazenamento de objetos guarda arquivos como objetos com chave e metadados, acessados por API HTTP. Veja como funciona, para que serve e seus limites.
- O que é S3 e armazenamento compatível com S3S3 é o armazenamento de objetos da Amazon e virou a API padrão do setor. Veja o que significa ser compatível com S3, o que muda e como conectar via boto3.
- Arquivo público ou privado: como controlar quem acessa seus arquivosArquivo público abre para quem tiver a URL; privado só por link autorizado. Veja como escolher, testar o acesso e compartilhar sem expor o arquivo.