Tutoriais6 min de leitura

Como Fazer Upload Multipart para Amazon S3 com Presigned URLs usando Django (Guia Completo)

Aprenda a implementar upload multipart para Amazon S3 utilizando Django, Presigned URLs e boto3. Faça uploads de arquivos grandes com segurança, alta performance e sem sobrecarregar seu backend.

Fundador

Fundador

@fundador

Como Fazer Upload Multipart para Amazon S3 com Django (Guia Completo)

Se sua aplicação permite que usuários enviem vídeos, backups, documentos ou qualquer arquivo grande, provavelmente você já enfrentou algum destes problemas:

  • uploads lentos;

  • timeout do servidor;

  • alto consumo de memória;

  • backend sobrecarregado;

  • uploads que precisam começar do zero quando a internet cai.

A boa notícia é que a Amazon S3 possui uma solução exatamente para isso: Multipart Upload.

Neste tutorial você aprenderá como implementar esse recurso utilizando Django, Amazon S3, Presigned URLs e boto3, seguindo uma arquitetura utilizada em aplicações de produção.



O que você vai construir

Ao final deste tutorial, o fluxo da aplicação será o seguinte:

                 Usuário
                    │
                    │ Seleciona um arquivo
                    ▼
             Frontend (React/Vue/JS)
                    │
                    │ POST /uploads/start
                    ▼
             Django REST API
                    │
                    │ CreateMultipartUpload()
                    ▼
                 Amazon S3
                    ▲
                    │
          retorna UploadId
                    │
                    ▼
            Frontend solicita
          Presigned URLs para
           cada parte do arquivo
                    │
                    ▼
        Upload paralelo das partes
             diretamente ao S3
                    │
                    ▼
          CompleteMultipartUpload
                    │
                    ▼
              Arquivo Final

Perceba um detalhe importante:

O arquivo nunca passa pelo backend.

O Django apenas coordena o processo.

Isso reduz drasticamente:


  • uso de CPU;


  • consumo de memória;


  • tráfego no servidor;


  • custo de infraestrutura;


  • tempo de resposta.

É exatamente essa arquitetura que serviços como Dropbox, Google Drive e diversos sistemas corporativos utilizam.


Como funciona o Multipart Upload?

Antes de escrever qualquer linha de código, vale entender o problema que estamos resolvendo.

Imagine um vídeo de 8 GB.

No upload tradicional, o navegador tenta enviar o arquivo inteiro em uma única requisição.

Cliente
    │
    │────────────── 8 GB ───────────────►
    │
Servidor

Se a conexão cair aos 7,9 GB, todo o processo precisa começar novamente.

Além disso:


  • um único request fica aberto por vários minutos;


  • proxies podem encerrar a conexão;


  • load balancers possuem timeout;


  • o servidor precisa manter recursos alocados durante todo o upload.

Agora veja o Multipart Upload.

O mesmo arquivo é dividido em pequenos blocos.

Parte 1   25 MB

Parte 2   25 MB

Parte 3   25 MB

...

Parte 320   25 MB

Cada parte é enviada independentemente.

Cliente

 ├────────► Parte 1
 ├────────► Parte 2
 ├────────► Parte 3
 ├────────► Parte 4
 ├────────► Parte 5

Depois que todas chegam ao S3, a própria AWS monta o arquivo final.

Isso traz diversas vantagens.




Upload paralelo

É possível enviar várias partes simultaneamente.

Thread 1 → Parte 1

Thread 2 → Parte 2

Thread 3 → Parte 3

Thread 4 → Parte 4

Em conexões rápidas isso pode reduzir bastante o tempo total.


Retomada de upload

Suponha que a parte 18 falhou.

Não é necessário reenviar tudo.

Apenas:

Parte 18

Esse é um dos maiores benefícios do Multipart Upload.


Menor uso do servidor

No modelo tradicional:

Cliente

↓

Backend

↓

S3

Todo o tráfego passa pelo servidor.

No modelo utilizando Presigned URLs:

Cliente

↓

S3

O backend apenas gera URLs temporárias.

Na prática, ele troca dezenas de gigabytes de tráfego por algumas chamadas de API com poucos kilobytes.


Como as Presigned URLs entram nessa história?

Essa é uma dúvida comum.

Se o navegador envia o arquivo diretamente para o S3, como impedir que qualquer pessoa faça upload no bucket?

A resposta é: Presigned URLs.

O backend gera uma URL assinada digitalmente.

Exemplo simplificado:

https://bucket.s3.amazonaws.com/video.mp4?
X-Amz-Algorithm=...
&X-Amz-Credential=...
&X-Amz-Date=...
&X-Amz-Signature=...

Essa URL possui características importantes:


  • validade de alguns minutos;


  • acesso limitado a uma operação específica;


  • permite enviar apenas uma parte do arquivo;


  • não expõe as credenciais da AWS.

Ou seja:

O frontend nunca conhece a Access Key nem a Secret Key da sua conta.



Fluxo completo da aplicação

Antes de programarmos, é importante visualizar todas as etapas.

Etapa 1

O usuário escolhe um arquivo.

video.mp4

4.2 GB

Etapa 2

O frontend chama:

POST /uploads/start

Nosso backend executará:

s3.create_multipart_upload(...)

Resposta:

{
    "upload_id": "abcd1234",
    "key": "videos/video.mp4"
}

O UploadId identifica aquele upload e será usado em todas as próximas etapas.


Etapa 3

Agora precisamos gerar URLs para cada parte.

Supondo um arquivo de 100 MB dividido em partes de 25 MB:

Parte 1

Parte 2

Parte 3

Parte 4

O frontend solicita:

POST /uploads/presigned-url

Resposta:

{
    "part_number": 1,
    "url": "https://..."
}

Depois repete o processo para as demais partes.


Etapa 4

Agora começa o upload real.

Cada parte é enviada diretamente ao S3.

Frontend

↓

PUT URL Parte 1

↓

Amazon S3

Nenhum byte passa pelo Django.


Etapa 5

Após cada upload, o S3 devolve um cabeçalho chamado ETag.

Exemplo:

ETag

"93f4ab90ef..."

Guarde esse valor.

Ele será necessário para finalizar o upload.


Etapa 6

Depois que todas as partes forem enviadas, o frontend chama:

POST /uploads/complete

Enviando algo semelhante a:

{
    "upload_id": "...",
    "parts": [
        {
            "PartNumber": 1,
            "ETag": "..."
        },
        {
            "PartNumber": 2,
            "ETag": "..."
        }
    ]
}

Nesse momento, o backend executa:

complete_multipart_upload()

A AWS então une todas as partes e cria o arquivo final.

Para o usuário, o upload está concluído.

Pré-requisitos

Antes de começarmos a implementação, certifique-se de ter:

  • Python 3.12 ou superior

  • Django 5

  • Django REST Framework

  • Conta na AWS

  • Bucket S3 criado

  • Usuário IAM com permissões para Multipart Upload

  • AWS CLI configurada (opcional, mas recomendada)

  • Conhecimentos básicos de APIs REST

    Criando o Bucket S3

    Se você já possui um bucket, pode utilizá-lo. Caso contrário, vamos criar um.

    Acesse o console da AWS e navegue até o serviço Amazon S3.

    Clique em Create Bucket.

    Escolha um nome globalmente único. Por exemplo:

    mycompany-uploads

    ou

    storage-blog-demo

    Escolha a região mais próxima dos seus usuários.

    Por exemplo:


    • us-east-1


    • sa-east-1


    • eu-west-1

    Dica: depois de colocar uma aplicação em produção, evite trocar de região. Isso pode aumentar a latência e complicar migrações futuras.


Bloqueio de acesso público

Na maioria dos casos, mantenha o Block Public Access ativado.

Isso significa que ninguém poderá acessar arquivos apenas conhecendo a URL.

Mais tarde, você poderá disponibilizar downloads utilizando Presigned URLs ou, se necessário, configurar uma CDN como o CloudFront.

Para aplicações corporativas, essa é a opção mais segura.


Criando um usuário IAM

Uma prática comum entre iniciantes é utilizar a conta raiz da AWS na aplicação.

Nunca faça isso.

Em vez disso:

AWS Console →

IAM →

Users →

Create User

Nome:

django-storage

Agora conceda apenas as permissões necessárias.


Política mínima

Em muitos tutoriais você verá algo parecido com:

{
    "Action": "s3:*",
    "Resource": "*"
}

Isso funciona.

Também é uma péssima ideia.

Estamos dando acesso completo a todos os buckets da conta.

O ideal é conceder apenas as permissões necessárias.

Exemplo:

{
  "Version":"2012-10-17",
  "Statement":[
    {
      "Effect":"Allow",
      "Action":[
        "s3:CreateMultipartUpload",
        "s3:UploadPart",
        "s3:CompleteMultipartUpload",
        "s3:AbortMultipartUpload",
        "s3:ListMultipartUploadParts",
        "s3:PutObject"
      ],
      "Resource":"arn:aws:s3:::storage-blog-demo/*"
    }
  ]
}

Observe que restringimos o acesso apenas ao bucket da aplicação.

Esse é um princípio importante de segurança chamado Princípio do Menor Privilégio (Least Privilege): conceda somente as permissões estritamente necessárias.


Obtendo as credenciais

Após criar o usuário, gere uma Access Key.

Você receberá:

AWS_ACCESS_KEY_ID

AWS_SECRET_ACCESS_KEY

Guarde essas informações em um local seguro.

Nunca publique essas chaves no GitHub.

Configurando as variáveis de ambiente

Crie um arquivo chamado .env.

AWS_ACCESS_KEY_ID=xxxxxxxx

AWS_SECRET_ACCESS_KEY=xxxxxxxx

AWS_STORAGE_BUCKET_NAME=storage-blog-demo

AWS_REGION=sa-east-1

Perceba que não colocamos essas informações no código.

Isso permite utilizar credenciais diferentes em desenvolvimento, homologação e produção.

Carregando o .env

No settings.py:

from dotenv import load_dotenv

import os

load_dotenv()

Agora podemos acessar qualquer variável:

AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID")

AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY")

AWS_STORAGE_BUCKET_NAME = os.getenv("AWS_STORAGE_BUCKET_NAME")

AWS_REGION = os.getenv("AWS_REGION")

Criando o cliente S3

Em uploads/services/s3.py:

import boto3

from django.conf import settings


s3_client = boto3.client(
    "s3",
    region_name=settings.AWS_REGION,
    aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
    aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
)

Separar essa lógica em um serviço facilita testes, reutilização e manutenção.

Criando o endpoint de início do upload

Vamos implementar a primeira etapa do fluxo:

Frontend

↓

POST /uploads/start

↓

AWS CreateMultipartUpload

↓

UploadId

Crie uploads/views.py:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

from .services.s3 import s3_client

from django.conf import settings


class StartMultipartUploadAPIView(APIView):

    def post(self, request):

        filename = request.data["filename"]

        response = s3_client.create_multipart_upload(
            Bucket=settings.AWS_STORAGE_BUCKET_NAME,
            Key=filename,
        )

        return Response(
            {
                "upload_id": response["UploadId"],
                "key": response["Key"],
            },
            status=status.HTTP_201_CREATED,
        )

Esse endpoint faz apenas uma chamada ao S3 para iniciar o upload multipart.

A resposta será semelhante a:

{
    "upload_id": "2~HjFk93L...",
    "key": "video.mp4"
}

O UploadId identifica de forma única aquela sessão de upload e será utilizado em todas as etapas seguintes.


Registrando a rota

Em uploads/urls.py:

from django.urls import path

from .views import StartMultipartUploadAPIView

urlpatterns = [
    path(
        "start/",
        StartMultipartUploadAPIView.as_view(),
        name="multipart-start",
    ),
]

Depois, inclua essas rotas no arquivo principal do projeto:

from django.urls import include, path

urlpatterns = [
    path("api/uploads/", include("uploads.urls")),
]

Agora você já consegue testar o primeiro endpoint.

Faça uma requisição:

POST /api/uploads/start
Content-Type: application/json

Corpo:

{
    "filename": "meu-video.mp4"
}

Resposta esperada:

{
    "upload_id": "2~HjFk93L...",
    "key": "meu-video.mp4"
}

🎉 Parabéns! Seu backend já consegue iniciar um upload multipart na AWS.

Leia também

Tutoriais2 min de leitura

Configurando um ambiente Node.js do zero em 2026

Do gerenciador de versões ao linter: um passo a passo para deixar seu ambiente de desenvolvimento Node.js sólido e reprodutível.

FundadorFundador
0💬 0

Comentários (0)

Entre na sua conta para participar da conversa.