sqlite3.Blob en Python permite leer y escribir partes de un campo BLOB de SQLite sin cargar todo el contenido en memoria. Es útil cuando una base de datos almacena imágenes, documentos, archivos comprimidos, modelos u otros datos binarios grandes. En lugar de ejecutar un SELECT que devuelve todos los bytes, la aplicación abre un manejador parecido a un archivo y procesa solo la región necesaria.
Qué representa sqlite3.Blob
Un objeto Blob es un acceso incremental a una columna BLOB existente. Se obtiene con Connection.blobopen. La interfaz ofrece read, write, seek, consulta de longitud, índices y slices. Los datos permanecen dentro de la fila de SQLite, mientras Python trabaja con bloques manejables. Esto reduce picos de memoria y facilita actualizaciones localizadas.
El manejador no cambia el tamaño del valor almacenado. Para reservar espacio, se inserta zeroblob con la cantidad de bytes deseada y luego se abre la columna para escritura. Escribir más allá del límite genera un error. Si el contenido debe crecer o reducirse, hay que crear un nuevo BLOB con el tamaño correcto y reemplazar la columna.
Crear la tabla y reservar espacio
import sqlite3
con = sqlite3.connect("archivos.db")
con.execute("CREATE TABLE IF NOT EXISTS adjuntos (id INTEGER PRIMARY KEY, nombre TEXT, datos BLOB)")
tamano = 1024 * 1024
cur = con.execute("INSERT INTO adjuntos(nombre, datos) VALUES (?, zeroblob(?))", ("ejemplo.bin", tamano))
rowid = cur.lastrowid
con.commit()zeroblob pide a SQLite que cree un valor binario lleno de ceros sin obligar a Python a construir primero un objeto bytes enorme. La fila obtiene un rowid estable que blobopen puede utilizar. Los nombres de tabla y columna deben coincidir con el esquema y no deberían proceder directamente de una entrada no confiable.
Abrir y escribir el BLOB
with con.blobopen("adjuntos", "datos", rowid, readonly=False) as blob:
blob.write(b"cabecera-binaria")
blob.seek(4096)
blob.write(b"contenido-en-otra-posicion")El context manager cierra el recurso incluso si ocurre una excepción. La primera escritura comienza en la posición cero. seek mueve el cursor, igual que en un archivo binario. Para transferencias grandes conviene usar bloques constantes, por ejemplo 64 KiB o 1 MiB, y registrar cuántos bytes se han escrito correctamente.
Lectura incremental
with con.blobopen("adjuntos", "datos", rowid, readonly=True) as blob:
total = len(blob)
while True:
parte = blob.read(64 * 1024)
if not parte:
break
procesar(parte)La lectura incremental es la ventaja principal. La aplicación puede calcular hashes, transmitir una respuesta, validar cabeceras o copiar el contenido a otro destino manteniendo estable el uso de memoria. En aplicaciones web esto reduce el consumo por petición, aunque las transacciones largas y los bloqueos siguen requiriendo cuidado.
Índices y slices
sqlite3.Blob permite acceder por índice. Un índice devuelve un entero entre 0 y 255, mientras un slice devuelve bytes. También se puede reemplazar un byte o una porción del mismo tamaño. Es práctico para cabeceras, flags y formatos binarios con posiciones fijas.
with con.blobopen("adjuntos", "datos", rowid) as blob:
primero = blob[0]
cabecera = blob[0:16]
blob[0] = 0x50
blob[1:4] = b"YTH"La asignación de una porción debe caber exactamente en el espacio seleccionado. El objeto no es una lista redimensionable: sobrescribe bytes existentes, no inserta contenido desplazando el resto.
Transacciones y concurrencia
SQLite utiliza transacciones y bloqueos de archivo. Un Blob abierto para escritura participa en ese contexto y puede retrasar commits u operaciones competidoras. Mantén el manejador abierto solo el tiempo necesario. Usa transacciones cortas, configura un timeout razonable y trata OperationalError. No compartas una conexión entre hilos sin una estrategia clara.
Si varios workers pueden modificar el mismo BLOB, define propiedad, bloqueo o una cola de escritura única. SQLite funciona muy bien en aplicaciones locales y cargas moderadas, pero no es un almacén de objetos distribuido. Para archivos enormes, muchos lectores concurrentes o entrega directa por CDN, puede ser mejor usar almacenamiento externo.
Validación e integridad
Valida rowid, nombre lógico, longitud esperada y permisos antes de abrir el BLOB. Después de escribir, calcula un hash criptográfico y compáralo con el valor previsto. Guarda tamaño, tipo MIME y checksum en columnas separadas para detectar truncamientos o contenido incorrecto.
import hashlib
h = hashlib.sha256()
with con.blobopen("adjuntos", "datos", rowid, readonly=True) as blob:
while parte := blob.read(65536):
h.update(parte)
print(h.hexdigest())Para ampliar el contexto, consulta las guías de Academify sobre SQLite con Python, manejo de archivos, pathlib en Python y excepciones en Python. También son referencias esenciales la documentación oficial de sqlite3 y la API incremental de BLOB de SQLite.
Copiar un archivo a SQLite
from pathlib import Path
origen = Path("archivo.bin")
tamano = origen.stat().st_size
cur = con.execute("INSERT INTO adjuntos(nombre, datos) VALUES (?, zeroblob(?))", (origen.name, tamano))
rowid = cur.lastrowid
with origen.open("rb") as src, con.blobopen("adjuntos", "datos", rowid) as dst:
while bloque := src.read(1024 * 1024):
dst.write(bloque)
con.commit()Este patrón mantiene estable la memoria porque solo conserva un bloque cada vez. Si falla la operación, ejecuta rollback y elimina la fila incompleta. Para un proceso reanudable, registra el desplazamiento confirmado y verifica cada bloque antes de continuar.
Cuándo usarlo
Usa sqlite3.Blob cuando el contenido binario pertenezca naturalmente a la misma transacción que otros datos, la aplicación sea local o tenga concurrencia moderada, el tamaño sea conocido y el acceso parcial aporte valor. Evítalo cuando los archivos sean gigantescos, los consulten muchos servidores o se administren mejor con almacenamiento de objetos.
Los backups también importan. Una base con muchos BLOB crece rápido y puede hacer más lentas las copias completas. Prueba restauraciones, retención, VACUUM y espacio libre. Una escritura correcta no garantiza una estrategia de recuperación adecuada.
Manejo de errores
Los fallos habituales incluyen una fila inexistente, nombres de tabla o columna incorrectos, escritura fuera del límite, cierre prematuro o base bloqueada. Captura excepciones específicas de sqlite3, registra rowid y operación, y evita guardar contenido binario sensible en logs. Usa context managers para liberar recursos de forma predecible.
Seguridad
No confíes en tipos MIME ni extensiones enviados por usuarios. Inspecciona firmas cuando corresponda, impone límites de tamaño antes de reservar zeroblob y restringe qué filas puede abrir cada usuario. Si los datos son sensibles, protege el archivo de base, sus backups y cualquier copia temporal.
Pruebas de rendimiento
Mide cargas reales. Compara SELECT completos con lecturas incrementales, prueba distintos tamaños de bloque y observa la duración de los bloqueos. Para BLOB pequeños quizá no compense la complejidad; para valores grandes el beneficio puede ser considerable.
Lista final de buenas prácticas
Abre el BLOB con context manager, reserva con zeroblob, procesa bloques fijos, valida tamaño y hash, mantén transacciones cortas y prueba interrupciones. Con estas prácticas, sqlite3.Blob ofrece acceso binario incremental eficiente sin perder la simplicidad de SQLite.







