zipfile no Python: arquivos ZIP seguros

Publicado em: 27/07/2026
Tempo de leitura: 5 minutos
Ícone de arquivo ZIP para artigo sobre zipfile no Python

O módulo zipfile no Python faz parte da biblioteca padrão e permite criar, ler, inspecionar e extrair arquivos ZIP sem instalar dependências externas. Ele é útil em backups, exportações de relatórios, distribuição de documentos, processamento de uploads e automações que precisam reunir vários arquivos em um único pacote.

Apesar de a API ser simples, um fluxo confiável precisa cuidar de nomes internos, tamanho dos arquivos, integridade, duplicatas e caminhos de extração. Neste guia, você aprenderá a trabalhar com ZipFile, escolher modos de abertura, escrever dados da memória, ler conteúdo sem extrair tudo, validar entradas e organizar uma rotina segura. O assunto se conecta a outros recursos da biblioteca padrão, como graphlib para dependências, contextvars para contexto seguro, heapq para filas de prioridade e singledispatch para APIs extensíveis.

Como abrir um arquivo ZIP

A classe principal é zipfile.ZipFile. O modo r abre um pacote para leitura, w cria ou substitui, a acrescenta conteúdo e x cria um novo arquivo, falhando quando o destino já existe.

from zipfile import ZipFile

with ZipFile("dados.zip", "r") as arquivo:
    print(arquivo.namelist())

O gerenciador de contexto fecha o arquivo corretamente. Isso é importante porque o ZIP mantém um diretório central com os metadados dos membros. Se a gravação não for finalizada, o pacote pode ficar incompleto.

Criando um pacote

Use write() para adicionar arquivos do disco. O argumento arcname define o nome interno e evita expor caminhos locais desnecessários.

from zipfile import ZIP_DEFLATED, ZipFile

with ZipFile("backup.zip", "w", compression=ZIP_DEFLATED) as arquivo:
    arquivo.write("relatorios/vendas.csv", arcname="vendas.csv")
    arquivo.write("config/app.json", arcname="config/app.json")

Defina nomes internos curtos e portáveis. Não armazene caminhos absolutos e evite incluir diretórios temporários ou nomes específicos do servidor.

Escrevendo conteúdo da memória

Quando o conteúdo já está em uma string ou sequência de bytes, writestr() dispensa um arquivo temporário.

from zipfile import ZIP_DEFLATED, ZipFile

texto = "id,nome\n1,Ana\n2,Caio\n"
with ZipFile("exportacao.zip", "w", ZIP_DEFLATED) as arquivo:
    arquivo.writestr("clientes.csv", texto)

Essa técnica funciona bem para relatórios pequenos gerados sob demanda. Para dados grandes, considere um arquivo temporário ou processamento em blocos para não manter tudo na memória.

Lendo sem extrair

O método read() devolve bytes de um membro. Já open() retorna um objeto semelhante a arquivo e permite processamento gradual.

from zipfile import ZipFile

with ZipFile("dados.zip") as arquivo:
    with arquivo.open("config.json") as entrada:
        texto = entrada.read().decode("utf-8")
        print(texto)

Essa abordagem é útil quando você quer enviar o conteúdo para um parser de JSON, CSV ou XML sem criar uma cópia no disco.

Inspecionando membros

infolist() retorna objetos ZipInfo com nome, tamanho original, tamanho compactado, data e método de compressão.

with ZipFile("upload.zip") as arquivo:
    for item in arquivo.infolist():
        print(item.filename, item.file_size, item.compress_size)

Inspecione essas informações antes de extrair. É possível limitar extensões, quantidade de membros, tamanho individual e tamanho total.

Validando caminhos

O nome de um membro deve ser tratado como entrada externa. Resolva o caminho final e confirme que ele permanece dentro do diretório autorizado.

from pathlib import Path
from zipfile import ZipFile

raiz = Path("recebidos").resolve()
with ZipFile("upload.zip") as arquivo:
    for item in arquivo.infolist():
        destino = (raiz / item.filename).resolve()
        if raiz not in destino.parents and destino != raiz:
            raise ValueError(f"Nome de arquivo inválido: {item.filename}")
        arquivo.extract(item, raiz)

Também rejeite caminhos absolutos, nomes vazios inesperados e componentes que não sejam necessários ao seu caso de uso.

Aplicando limites de recursos

Um arquivo compactado pequeno pode expandir para uma quantidade muito maior de dados. Defina limites por membro e por pacote.

MAX_ARQUIVO = 50 * 1024 * 1024
MAX_TOTAL = 200 * 1024 * 1024

total = 0
for item in arquivo.infolist():
    if item.file_size > MAX_ARQUIVO:
        raise ValueError("Arquivo interno acima do limite")
    total += item.file_size
    if total > MAX_TOTAL:
        raise ValueError("Pacote acima do limite total")

Durante a cópia, conte os bytes realmente lidos. Assim, a aplicação pode interromper a operação caso os metadados não correspondam ao conteúdo.

Verificando integridade

testzip() lê os membros e verifica o CRC. Ele retorna o primeiro nome com problema ou None quando tudo está consistente.

from zipfile import BadZipFile, ZipFile

try:
    with ZipFile("arquivo.zip") as arquivo:
        problema = arquivo.testzip()
        if problema:
            raise ValueError(f"Membro corrompido: {problema}")
except BadZipFile:
    print("Arquivo ZIP inválido")

Em APIs, registre os detalhes internamente e devolva ao cliente uma mensagem simples, sem expor caminhos do servidor.

Escolhendo a compressão

ZIP_DEFLATED é uma opção amplamente compatível. BZIP2 e LZMA podem reduzir mais alguns tipos de dados, mas nem todos os consumidores antigos oferecem suporte. Imagens JPEG, vídeos e arquivos já compactados costumam apresentar pouco ganho.

O parâmetro compresslevel controla o equilíbrio entre velocidade e tamanho. Meça o resultado com dados reais antes de aumentar o nível em processos frequentes.

Tratando nomes duplicados

Um pacote pode conter mais de uma entrada com o mesmo nome. Isso pode gerar resultados diferentes entre ferramentas. Normalize e registre os nomes antes da extração.

vistos = set()
for item in arquivo.infolist():
    nome = item.filename.replace("\\", "/").casefold()
    if nome in vistos:
        raise ValueError(f"Nome duplicado: {item.filename}")
    vistos.add(nome)

Considere diferenças entre maiúsculas e minúsculas, barras e caracteres Unicode equivalentes.

Extração controlada

Para entradas externas, prefira inspecionar e copiar um membro por vez. Isso permite aplicar regras, acompanhar progresso e remover arquivos parciais em caso de falha.

import shutil

for item in arquivo.infolist():
    destino = raiz / item.filename
    if item.is_dir():
        destino.mkdir(parents=True, exist_ok=True)
        continue
    destino.parent.mkdir(parents=True, exist_ok=True)
    with arquivo.open(item) as origem, destino.open("wb") as saida:
        shutil.copyfileobj(origem, saida, length=1024 * 1024)

Arquivos protegidos por senha

A biblioteca padrão consegue ler alguns ZIPs criptografados tradicionais, mas não é adequada para proteção moderna de dados sensíveis. Para confidencialidade real, use ferramentas e formatos com criptografia atual e gestão correta de chaves.

Testes importantes

Inclua testes para pacote vazio, arquivo inexistente, ZIP corrompido, nomes duplicados, diretórios, caracteres acentuados, arquivos grandes, limite total e caminho inválido. Verifique também se arquivos parciais são removidos após uma exceção.

Boas práticas

  • Use with em todas as operações.
  • Defina nomes internos com arcname.
  • Valide caminhos antes da extração.
  • Limite tamanho individual, total e quantidade de membros.
  • Rejeite nomes duplicados e extensões proibidas.
  • Verifique a integridade quando necessário.
  • Extraia em diretório temporário isolado.
  • Não execute automaticamente arquivos extraídos.
  • Registre falhas e limpe resultados incompletos.
  • Teste com entradas válidas e inválidas.

Conclusão

O zipfile no Python cobre os principais casos de criação e leitura de arquivos ZIP. Para produção, combine a API com validação de nomes, limites de recursos, inspeção de metadados e extração controlada.

Ao tratar cada membro como entrada externa e verificar o destino antes de gravar, sua aplicação fica mais previsível e resistente a pacotes problemáticos. Consulte a documentação oficial de zipfile e as orientações da OWASP sobre extração segura.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Grafo de dependências e fluxo de tarefas em Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    graphlib no Python: ordenação topológica

    Aprenda graphlib no Python para ordenar dependências, detectar ciclos e executar tarefas independentes em paralelo.

    Ler mais

    Tempo de leitura: 6 minutos
    27/07/2026
    Código Python para contexto seguro em aplicações assíncronas
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    contextvars no Python: contexto seguro

    Aprenda a usar contextvars no Python para isolar contexto em asyncio, logs, threads e testes sem depender de variáveis globais.

    Ler mais

    Tempo de leitura: 7 minutos
    26/07/2026
    Código Python usando cached_property para armazenar cálculos
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    cached_property no Python: cache em objetos

    Aprenda cached_property no Python para armazenar cálculos caros, invalidar valores e evitar caches desatualizados em objetos.

    Ler mais

    Tempo de leitura: 7 minutos
    25/07/2026
    Código Python com funções especializadas por tipo
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    singledispatch no Python: polimorfismo simples

    Aprenda singledispatch no Python para criar funções por tipo, reduzir isinstance e organizar polimorfismo com exemplos práticos.

    Ler mais

    Tempo de leitura: 6 minutos
    25/07/2026
    Código Python com descriptors e atributos
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    Descriptors em Python: guia prático

    Aprenda descriptors em Python com __get__, __set__, validação, property, armazenamento por instância, testes e boas práticas.

    Ler mais

    Tempo de leitura: 6 minutos
    22/07/2026
    Criando instalador EXE com ícone personalizado em Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    Como criar um instalador .exe com ícone personalizado no Python

    Se você já desenvolveu algum script útil, provavelmente já se perguntou como criar um instalador .exe com ícone personalizado no

    Ler mais

    Tempo de leitura: 11 minutos
    25/04/2026