zipfile en Python: archivos ZIP seguros

Publicado el: 27/07/2026
Tempo de leitura: 5 minutos
Icono de archivo ZIP para un artículo sobre zipfile en Python

El módulo zipfile en Python forma parte de la biblioteca estándar y permite crear, leer, inspeccionar y extraer archivos ZIP sin instalar paquetes externos. Es útil para copias de seguridad, exportación de informes, distribución de documentos, procesamiento de cargas y automatizaciones que necesitan reunir muchos archivos en un único contenedor.

Aunque la API es sencilla, un flujo fiable debe controlar los nombres internos, el tamaño total, las entradas duplicadas, la integridad y las rutas de extracción. En esta guía aprenderás a usar ZipFile, elegir modos de apertura, escribir contenido en memoria, leer archivos sin extraer todo, inspeccionar metadatos y aplicar validaciones prácticas. El tema complementa otros recursos de la biblioteca estándar, como graphlib para dependencias, contextvars para contexto seguro, heapq para colas de prioridad y singledispatch para API extensibles.

Abrir un archivo ZIP

La clase principal es zipfile.ZipFile. El modo r abre un archivo existente para lectura, w crea o reemplaza, a agrega contenido y x crea un archivo nuevo y falla si el destino ya existe.

from zipfile import ZipFile

with ZipFile("datos.zip", "r") as archivo:
    print(archivo.namelist())

Conviene utilizar siempre un administrador de contexto. Los ZIP mantienen un directorio central con los metadatos de sus miembros y un archivo que no se cierra correctamente puede quedar incompleto.

Crear un paquete

Usa write() para añadir un archivo del disco. El argumento arcname define el nombre interno guardado en el ZIP.

from zipfile import ZIP_DEFLATED, ZipFile

with ZipFile("respaldo.zip", "w", compression=ZIP_DEFLATED) as archivo:
    archivo.write("informes/ventas.csv", arcname="ventas.csv")
    archivo.write("config/app.json", arcname="config/app.json")

Elige nombres internos cortos y portables. Evita rutas absolutas y directorios específicos del servidor porque dificultan el traslado del archivo y pueden revelar estructura local.

Escribir contenido en memoria

Cuando los datos ya existen como texto o bytes, writestr() evita crear un archivo temporal.

from zipfile import ZIP_DEFLATED, ZipFile

contenido = "id,nombre\n1,Ana\n2,Caio\n"
with ZipFile("exportacion.zip", "w", ZIP_DEFLATED) as archivo:
    archivo.writestr("clientes.csv", contenido)

Esta técnica funciona bien para informes pequeños generados bajo demanda. Para salidas grandes, utiliza archivos temporales o procesamiento por bloques para no conservar todo el resultado en memoria.

Leer sin extraer

El método read() devuelve un miembro como bytes. open() devuelve un objeto similar a un archivo y permite procesar el contenido de forma gradual.

from zipfile import ZipFile

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

Esto es útil para enviar el contenido directamente a un analizador de JSON, CSV, XML o texto sin escribir otra copia en el disco.

Inspeccionar miembros

infolist() devuelve objetos ZipInfo con nombre, tamaño original, tamaño comprimido, fecha y método de compresión.

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

Revisa los metadatos antes de extraer. La aplicación puede rechazar extensiones no permitidas, demasiadas entradas, miembros demasiado grandes o relaciones de compresión poco habituales.

Validar rutas de extracción

El nombre de un miembro debe tratarse como entrada externa. Resuelve el destino final y comprueba que permanezca dentro del directorio autorizado.

from pathlib import Path
from zipfile import ZipFile

raiz = Path("recibidos").resolve()
with ZipFile("carga.zip") as archivo:
    for item in archivo.infolist():
        destino = (raiz / item.filename).resolve()
        if raiz not in destino.parents and destino != raiz:
            raise ValueError(f"Ruta inválida: {item.filename}")
        archivo.extract(item, raiz)

También conviene rechazar nombres absolutos, componentes innecesarios y tipos de archivo que la aplicación no necesite.

Aplicar límites de recursos

Un archivo comprimido pequeño puede expandirse hasta ocupar mucho más espacio. Define un máximo por miembro y otro para el paquete completo.

MAX_ARCHIVO = 50 * 1024 * 1024
MAX_TOTAL = 200 * 1024 * 1024

total = 0
for item in archivo.infolist():
    if item.file_size > MAX_ARCHIVO:
        raise ValueError("Miembro demasiado grande")
    total += item.file_size
    if total > MAX_TOTAL:
        raise ValueError("El paquete supera el límite total")

Cuenta también los bytes realmente copiados. Así la aplicación puede detenerse si los metadatos declarados no coinciden con el flujo procesado.

Comprobar integridad

testzip() lee los miembros y verifica su CRC. Devuelve el primer nombre con problemas o None cuando no encuentra errores.

from zipfile import BadZipFile, ZipFile

try:
    with ZipFile("archivo.zip") as archivo:
        dañado = archivo.testzip()
        if dañado:
            raise ValueError(f"Miembro dañado: {dañado}")
except BadZipFile:
    print("El archivo no es un ZIP válido")

En una API, registra los detalles técnicos internamente y devuelve al cliente un mensaje sencillo.

Elegir la compresión

ZIP_DEFLATED ofrece amplia compatibilidad y funciona bien con texto y datos estructurados. BZIP2 y LZMA pueden reducir más algunos contenidos, pero herramientas antiguas quizá no los soporten. Imágenes JPEG, vídeos y archivos ya comprimidos suelen mejorar poco.

El parámetro compresslevel controla el equilibrio entre velocidad y tamaño. Mide con datos reales antes de aumentar el nivel en tareas frecuentes.

Tratar nombres duplicados

Un ZIP puede contener varias entradas con el mismo nombre. Distintas herramientas pueden elegir entradas diferentes, por lo que conviene normalizar y registrar los nombres antes de extraer.

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

Considera diferencias entre mayúsculas y minúsculas, tipos de barra y representaciones Unicode equivalentes.

Extracción controlada

Para archivos externos, inspecciona y copia un miembro cada vez. Esto permite aplicar límites, mostrar progreso y eliminar resultados parciales si ocurre un error.

import shutil

for item in archivo.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 archivo.open(item) as origen, destino.open("wb") as salida:
        shutil.copyfileobj(origen, salida, length=1024 * 1024)

Archivos con contraseña

La biblioteca estándar puede leer algunos ZIP con cifrado tradicional, pero no es la mejor opción para requisitos modernos de confidencialidad. La información sensible necesita herramientas actuales, una gestión correcta de claves y un modelo de amenazas claro.

Pruebas importantes

Incluye pruebas para archivos vacíos, datos ZIP inválidos, nombres duplicados, directorios, nombres Unicode, miembros grandes, límites totales y rutas no permitidas. Comprueba también que los archivos parciales se eliminen después de una excepción.

Buenas prácticas

  • Usa with en todas las operaciones.
  • Define nombres internos con arcname.
  • Valida la ruta final antes de escribir.
  • Limita cantidad de miembros, tamaño individual y tamaño total.
  • Rechaza nombres duplicados y extensiones no permitidas.
  • Comprueba la integridad cuando sea importante.
  • Extrae en un directorio temporal aislado.
  • No ejecutes automáticamente archivos extraídos.
  • Registra errores y elimina resultados incompletos.
  • Prueba paquetes normales y problemáticos.

Conclusión

zipfile en Python cubre los casos más habituales para crear y consumir archivos ZIP. Una implementación de producción debe combinar la API básica con inspección de metadatos, límites de recursos, validación de rutas y extracción controlada.

Al tratar cada miembro como entrada externa y verificar el destino antes de escribir, la aplicación se vuelve más predecible y resistente. Consulta la documentación oficial de zipfile y la guía de OWASP sobre extracción segura.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Programador analizando código para identificar tipos MIME de archivos con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    mimetypes.guess_file_type: detecta tipos MIME

    Aprende mimetypes.guess_file_type en Python para detectar tipos MIME en rutas, URLs, uploads y respuestas HTTP con fallbacks seguros.

    Ler mais

    Tempo de leitura: 4 minutos
    13/09/2026
    Componentes de servidor que representan intérpretes Python aislados ejecutándose en paralelo
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    InterpreterPoolExecutor: paralelismo real en Python

    Aprende InterpreterPoolExecutor en Python para tareas CPU-bound, intérpretes aislados, paralelismo real y concurrencia segura.

    Ler mais

    Tempo de leitura: 6 minutos
    13/09/2026
    Microprocesador que representa las CPU disponibles para un proceso Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    os.process_cpu_count: cuenta CPUs disponibles

    Aprende os.process_cpu_count en Python para dimensionar workers según las CPU disponibles para el proceso.

    Ler mais

    Tempo de leitura: 4 minutos
    12/09/2026
    Portátil con código digital que representa datos BLOB en SQLite
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    sqlite3.Blob: lee BLOBs sin cargar todo en memoria

    Aprende sqlite3.Blob en Python para leer y escribir BLOBs por partes, reducir memoria y manejar datos binarios en SQLite.

    Ler mais

    Tempo de leitura: 5 minutos
    12/09/2026
    Análisis estadístico para random.binomialvariate en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    random.binomialvariate: simula resultados binomiales

    Aprende random.binomialvariate en Python para simular éxitos, validar probabilidades y analizar escenarios binomiales con ejemplos.

    Ler mais

    Tempo de leitura: 5 minutos
    11/09/2026
    Código Python y análisis de firmas de funciones
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    inspect.signature.bind: valida argumentos de funciones

    Aprende inspect.signature.bind en Python para validar argumentos, aplicar valores predeterminados y crear APIs dinámicas seguras.

    Ler mais

    Tempo de leitura: 5 minutos
    11/09/2026