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
withem 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.






