ZetCode

Python sqlite3.Blob.read Method

Last modified April 15, 2025

This comprehensive guide explores Python's sqlite3.Blob.read method, used for reading binary data from SQLite BLOB columns. We'll cover basic usage, parameters, and practical examples with proper resource management.

Basic Definitions

The sqlite3.Blob class represents binary large objects (BLOBs) in SQLite databases. It provides methods to read and write binary data efficiently.

The read method retrieves binary data from a BLOB column. It accepts parameters to control how much data to read and where to start reading from.

Basic BLOB Reading

This example shows how to read an entire BLOB from a database using the read method with proper resource management.

basic_read.py
import sqlite3

# Create a database with BLOB data
with sqlite3.connect('blobs.db') as conn:
    conn.execute("CREATE TABLE IF NOT EXISTS images (id INTEGER PRIMARY KEY, data BLOB)")
    
    # Insert sample binary data
    with open('sample.png', 'rb') as f:
        conn.execute("INSERT INTO images (data) VALUES (?)", (f.read(),))
    
    # Read the BLOB
    with conn.blobopen('images', 'data', 1, 'main') as blob:
        image_data = blob.read()
        print(f"Read {len(image_data)} bytes from BLOB")

print("Database connection automatically closed")

This example creates a database with a BLOB column, inserts binary data from a file, then reads it back using blob.read. The with statements ensure proper resource cleanup.

The blobopen method creates a Blob object for the specified table, column, and row. The read method reads all data when called without parameters.

Reading Partial BLOB Data

The read method can read specific portions of a BLOB by specifying size and offset parameters.

partial_read.py
import sqlite3

with sqlite3.connect('partial.db') as conn:
    conn.execute("CREATE TABLE IF NOT EXISTS documents (id INTEGER, content BLOB)")
    
    # Insert large binary data
    large_data = b'X' * 1024 * 1024  # 1MB of data
    conn.execute("INSERT INTO documents VALUES (1, ?)", (large_data,))
    
    # Read first 100 bytes
    with conn.blobopen('documents', 'content', 1, 'main') as blob:
        first_chunk = blob.read(100)
        print(f"First 100 bytes: {len(first_chunk)}")
        
        # Read next 200 bytes from offset 100
        next_chunk = blob.read(200, 100)
        print(f"Next 200 bytes: {len(next_chunk)}")

print("Resources automatically released")

This example demonstrates reading specific portions of a large BLOB. The first read gets the first 100 bytes, while the second reads 200 bytes starting from offset 100.

Partial reading is useful for processing large BLOBs without loading everything into memory at once, improving memory efficiency.

Reading BLOB in Chunks

For very large BLOBs, you can read data in chunks using a loop with the read method.

chunked_read.py
import sqlite3

CHUNK_SIZE = 4096  # 4KB chunks

with sqlite3.connect('large_blobs.db') as conn:
    conn.execute("CREATE TABLE IF NOT EXISTS videos (id INTEGER, frames BLOB)")
    
    # Insert sample video data (simulated)
    video_data = b'FRAME' * 100000  # ~500KB
    conn.execute("INSERT INTO videos VALUES (1, ?)", (video_data,))
    
    # Process in chunks
    with conn.blobopen('videos', 'frames', 1, 'main') as blob:
        total_read = 0
        while True:
            chunk = blob.read(CHUNK_SIZE, total_read)
            if not chunk:
                break
            total_read += len(chunk)
            print(f"Processed chunk {len(chunk)} bytes, total {total_read}")

print(f"Finished processing {total_read} bytes")

This example reads a large BLOB in 4KB chunks, processing each chunk individually. The loop continues until read returns an empty bytes object.

Chunked reading is essential for handling very large binary objects while maintaining reasonable memory usage in your application.

Reading BLOB with Different Offsets

The read method's offset parameter allows reading from any position in the BLOB, enabling random access to binary data.

offset_read.py
import sqlite3

with sqlite3.connect('offset.db') as conn:
    conn.execute("CREATE TABLE IF NOT EXISTS binary (id INTEGER, data BLOB)")
    
    # Insert patterned data
    data = bytes(range(256))  # 0-255 byte values
    conn.execute("INSERT INTO binary VALUES (1, ?)", (data,))
    
    # Read from various offsets
    with conn.blobopen('binary', 'data', 1, 'main') as blob:
        # Read first 10 bytes
        print("Start:", blob.read(10))
        
        # Read 10 bytes from middle
        print("Middle:", blob.read(10, 128))
        
        # Read last 10 bytes
        print("End:", blob.read(10, 246))

print("All reads completed")

This example inserts a BLOB containing bytes 0-255, then demonstrates reading from different positions. Each read operation specifies both size and offset.

Random access is particularly useful for formats with known structures where you need to read specific parts of binary data without processing everything.

Handling Empty BLOBs

The read method behaves differently with empty BLOBs, returning an empty bytes object. This example shows proper handling.

empty_blob.py
import sqlite3

with sqlite3.connect('empty.db') as conn:
    conn.execute("CREATE TABLE IF NOT EXISTS files (name TEXT, content BLOB)")
    
    # Insert empty BLOB
    conn.execute("INSERT INTO files VALUES ('empty.txt', ?)", (b'',))
    
    # Attempt to read
    with conn.blobopen('files', 'content', 1, 'main') as blob:
        data = blob.read()
        if not data:
            print("BLOB is empty")
        else:
            print(f"Read {len(data)} bytes")

print("Database connection closed")

This example inserts an empty BLOB (zero-length bytes) and demonstrates how to check for this condition. The read method returns b'' for empty BLOBs.

Always check the return value of read when working with potentially empty BLOBs to avoid processing empty data as valid content.

Error Handling with BLOB Reading

This example demonstrates proper error handling when reading BLOBs, including cases where the BLOB doesn't exist or is invalid.

error_handling.py
import sqlite3
from contextlib import suppress

with sqlite3.connect('errors.db') as conn:
    conn.execute("CREATE TABLE IF NOT EXISTS resources (id INTEGER, payload BLOB)")
    
    try:
        # Attempt to read non-existent BLOB
        with conn.blobopen('resources', 'payload', 999, 'main') as blob:
            data = blob.read()
            print(f"Read {len(data)} bytes")
    except sqlite3.OperationalError as e:
        print(f"Error reading BLOB: {e}")
    
    # Insert invalid data (simulate corruption)
    conn.execute("INSERT INTO resources VALUES (1, 'not a blob')")
    
    with suppress(sqlite3.OperationalError):
        with conn.blobopen('resources', 'payload', 1, 'main') as blob:
            data = blob.read()
            print("This won't print for invalid BLOB")

print("Cleanup complete")

This example shows two error scenarios: reading a non-existent row and attempting to read invalid BLOB data. The first uses try/except, the second uses suppress.

Proper error handling is crucial when working with BLOBs as invalid data or missing rows can cause exceptions that need to be caught and handled gracefully.

Best Practices

Source References

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Python tutorials.