sqlite3.Blob no Python permite ler e gravar partes de um campo BLOB sem carregar todo o conteúdo na memória. Esse recurso é útil quando um banco SQLite armazena imagens, documentos, arquivos compactados, modelos ou blocos binários grandes. Em vez de executar um SELECT que devolve todos os bytes de uma vez, a aplicação abre um objeto semelhante a arquivo, movimenta o cursor e acessa somente o trecho necessário.
O que é sqlite3.Blob
O objeto Blob representa um manipulador incremental para uma coluna BLOB existente. Ele é obtido com o método blobopen da conexão. A API oferece leitura, escrita, posicionamento com seek, consulta do tamanho com len e acesso por índices ou fatias. O comportamento lembra um arquivo binário, mas os dados permanecem dentro da linha da tabela SQLite. Isso reduz picos de memória e simplifica atualizações localizadas.
O recurso não cria automaticamente um BLOB de tamanho arbitrário. Para reservar espaço, a prática recomendada é inserir zeroblob com o tamanho desejado e, em seguida, abrir esse campo para escrita. O tamanho do BLOB aberto é fixo durante a sessão; tentar escrever além do limite gera erro. Se for necessário aumentar ou reduzir o conteúdo, crie outro valor com o novo tamanho e substitua a coluna.
Criando a tabela e reservando espaço
import sqlite3
con = sqlite3.connect("arquivos.db")
con.execute("CREATE TABLE IF NOT EXISTS anexos (id INTEGER PRIMARY KEY, nome TEXT, dados BLOB)")
tamanho = 1024 * 1024
cur = con.execute("INSERT INTO anexos(nome, dados) VALUES (?, zeroblob(?))", ("exemplo.bin", tamanho))
rowid = cur.lastrowid
con.commit()zeroblob cria um valor binário preenchido com zeros sem exigir que a aplicação monte uma sequência gigantesca em Python. Isso é especialmente importante para arquivos grandes. Depois do commit, a linha existe e pode ser aberta pelo rowid. O nome da tabela e da coluna deve corresponder exatamente ao esquema usado.
Abrindo e escrevendo o BLOB
with con.blobopen("anexos", "dados", rowid, readonly=False) as blob:
bloco = b"cabecalho-binario"
blob.write(bloco)
blob.seek(4096)
blob.write(b"dados-em-outra-posicao")O context manager fecha o manipulador mesmo quando ocorre uma exceção. Isso evita recursos abertos por mais tempo que o necessário. O primeiro write grava na posição atual, inicialmente zero. seek move o cursor para um deslocamento absoluto ou relativo, seguindo a mesma ideia dos arquivos tradicionais. Para sistemas que escrevem em blocos, escolha tamanhos consistentes, como 64 KiB ou 1 MiB, e registre quantos bytes foram efetivamente persistidos.
Lendo em partes
with con.blobopen("anexos", "dados", rowid, readonly=True) as blob:
total = len(blob)
while True:
parte = blob.read(64 * 1024)
if not parte:
break
processar(parte)A leitura incremental é a principal vantagem. O programa pode calcular hashes, enviar dados por streaming, validar cabeçalhos ou copiar conteúdo para outro destino sem manter tudo em RAM. Em aplicações web, essa abordagem ajuda a reduzir o consumo por requisição, embora seja necessário cuidar do tempo de transação e do bloqueio do banco.
Índices e fatias
sqlite3.Blob também aceita acesso por índice. Um índice retorna um inteiro entre 0 e 255, enquanto uma fatia retorna bytes. É possível substituir uma posição ou uma fatia de tamanho compatível. Esse recurso é conveniente para corrigir cabeçalhos, flags e campos fixos em formatos binários.
with con.blobopen("anexos", "dados", rowid) as blob:
primeiro = blob[0]
cabecalho = blob[0:16]
blob[0] = 0x50
blob[1:4] = b"YTH"A fatia atribuída precisa respeitar o espaço selecionado. Não use essa API como se fosse uma lista redimensionável. Ela modifica bytes existentes, não insere novos bytes deslocando o restante.
Transações e concorrência
SQLite trabalha com transações e bloqueios de arquivo. Um BLOB aberto para escrita mantém um contexto que pode influenciar commits e outras operações. Por isso, mantenha o manipulador aberto apenas pelo tempo necessário. Em aplicações concorrentes, use transações curtas, configure timeout adequado e trate OperationalError. Não compartilhe indiscriminadamente a mesma conexão entre threads. Consulte também o guia de SQLite com Python e o artigo sobre tratamento de exceções no Python.
Se várias rotinas podem atualizar o mesmo BLOB, estabeleça uma regra de exclusão ou uma fila de gravação. O SQLite é excelente para aplicações locais e cargas moderadas, mas não substitui um serviço de objetos distribuído. Para arquivos enormes, alta concorrência ou acesso remoto, armazenar o binário fora do banco e manter apenas caminho, hash e metadados pode ser mais simples.
Validação e integridade
Antes de abrir o BLOB, valide o rowid, o nome lógico do arquivo e a permissão do usuário. Depois da escrita, calcule um hash e compare com o valor esperado. Também é útil armazenar tamanho real, tipo MIME e checksum em colunas separadas. Assim, a aplicação detecta conteúdo truncado ou incompatível.
import hashlib
h = hashlib.sha256()
with con.blobopen("anexos", "dados", rowid, readonly=True) as blob:
for _ in range(0, len(blob), 65536):
h.update(blob.read(65536))
print(h.hexdigest())Para aprofundar a leitura de arquivos e buffers, veja manipulação de arquivos no Python e pathlib no Python. A documentação oficial do módulo sqlite3 e a documentação do BLOB incremental do SQLite são referências essenciais.
Copiando um arquivo para o banco
from pathlib import Path
origem = Path("arquivo.bin")
tamanho = origem.stat().st_size
cur = con.execute("INSERT INTO anexos(nome, dados) VALUES (?, zeroblob(?))", (origem.name, tamanho))
rowid = cur.lastrowid
with origem.open("rb") as src, con.blobopen("anexos", "dados", rowid) as dst:
while bloco := src.read(1024 * 1024):
dst.write(bloco)
con.commit()Esse padrão mantém a memória estável porque apenas um bloco existe por vez. Em caso de falha, execute rollback e remova a linha incompleta. Para tornar o processo retomável, registre o deslocamento já confirmado e compare os blocos gravados.
Quando usar e quando evitar
Use sqlite3.Blob quando o conteúdo já pertence naturalmente à transação do banco, quando a aplicação é local, quando o tamanho é conhecido e quando leituras ou atualizações parciais trazem benefício. Evite quando os arquivos são gigantescos, acessados por muitos servidores, entregues diretamente por CDN ou administrados melhor por um sistema de arquivos ou armazenamento de objetos.
Também considere backups. Um banco com muitos BLOBs cresce rapidamente e pode tornar cópias completas mais demoradas. Teste VACUUM, estratégia de retenção e recuperação. O artigo sobre backup de banco de dados com Python ajuda a estruturar esse cuidado.
Boas práticas finais
Abra o BLOB com context manager, reserve espaço com zeroblob, grave em blocos, valide tamanho e hash, mantenha transações curtas e nunca confie apenas no nome do arquivo. Trate erros, feche a conexão e teste interrupções. Com essas práticas, sqlite3.Blob oferece uma solução eficiente para acesso binário incremental sem sacrificar clareza ou segurança.







