How do I save images in SQLite database? Full Developer Payload for Python

онлайн тренажер по питону
Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Storing Images in SQLite Databases with Python: A Comprehensive Guide

Working with databases is an essential part of modern programming. When it comes to storing images in a database, many developers have questions about the proper way to implement this process. SQLite is a popular embedded database. Despite its simplicity, it excels at storing binary data, including images.

In this guide, you'll learn how to store and retrieve images in an SQLite database using Python. We'll also explore how to use the sqlite update python command to modify already stored data.

Fundamentals of Image Storage in SQLite

SQLite databases provide the ability to store binary data through a special type called BLOB (Binary Large Object). This data type is used to store images, audio files, video, and other types of files.

BLOB Data Type in SQLite

A BLOB in SQLite can store data of arbitrary size. This data type is ideal for working with images of various formats: JPEG, PNG, GIF, BMP, and many others. When working with BLOBs, it's important to remember that the data is stored in the form in which it was written.

Advantages and Disadvantages of Storing Images in a Database

Advantages:

  • Data integrity is ensured by database mechanisms.
  • Simplified backup of the entire system.
  • Access control through SQL user rights.
  • Transactional security of operations.

Disadvantages:

  • Significant increase in the size of the database file.
  • Reduced performance when frequently retrieving large files.
  • Complicated caching of images by web servers.
  • Limitations on the size of individual records.

When to Store Images in a Database

Storing images directly in the database is justified in the following cases:

  • Small number of images of small size.
  • Critical requirements for data integrity.
  • Need to ensure a strict relationship between the image and the record.
  • Lack of ability to use the file system.

Setting Up the Environment and Preparing to Work

Importing Necessary Libraries

import sqlite3
import os
from pathlib import Path

Creating a Database and Table for Images

def create_database():
    conn = sqlite3.connect('images.db')
    cursor = conn.cursor()
    
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS images (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            file_extension TEXT,
            file_size INTEGER,
            upload_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            data BLOB NOT NULL
        )
    ''')
    
    conn.commit()
    conn.close()

The extended table structure includes additional fields for storing metadata about the images. The file_extension field helps when restoring files, file_size allows you to control the size of the data, and upload_date provides a history of uploads.

Saving Images to an SQLite Database

Function for Converting an Image to Binary Data

def convert_to_binary(file_path):
    try:
        with open(file_path, 'rb') as file:
            binary_data = file.read()
        return binary_data
    except FileNotFoundError:
        print(f"File {file_path} not found")
        return None
    except PermissionError:
        print(f"No access rights to file {file_path}")
        return None

Function for Saving an Image with Checks

def insert_image(name, file_path):
    if not os.path.exists(file_path):
        print("Specified file does not exist")
        return False
    
    file_extension = Path(file_path).suffix.lower()
    file_size = os.path.getsize(file_path)
    
    # Check for maximum file size (e.g., 10 MB)
    max_size = 10 * 1024 * 1024
    if file_size > max_size:
        print(f"File is too large. Maximum size: {max_size} bytes")
        return False
    
    binary_data = convert_to_binary(file_path)
    if binary_data is None:
        return False
    
    try:
        conn = sqlite3.connect('images.db')
        cursor = conn.cursor()
        
        cursor.execute('''
            INSERT INTO images (name, file_extension, file_size, data)
            VALUES (?, ?, ?, ?)
        ''', (name, file_extension, file_size, binary_data))
        
        conn.commit()
        image_id = cursor.lastrowid
        conn.close()
        
        print(f"Image successfully saved with ID: {image_id}")
        return True
        
    except sqlite3.Error as e:
        print(f"Error working with the database: {e}")
        return False

Batch Loading of Images

def insert_multiple_images(image_list):
    successful_uploads = 0
    failed_uploads = 0
    
    for name, file_path in image_list:
        if insert_image(name, file_path):
            successful_uploads += 1
        else:
            failed_uploads += 1
    
    print(f"Successfully uploaded: {successful_uploads}")
    print(f"Failed to upload: {failed_uploads}")

Extracting Images from the Database

Basic Image Extraction Function

def retrieve_image(image_id, output_path):
    try:
        conn = sqlite3.connect('images.db')
        cursor = conn.cursor()
        
        cursor.execute('''
            SELECT data, file_extension 
            FROM images 
            WHERE id = ?
        ''', (image_id,))
        
        record = cursor.fetchone()
        
        if record:
            binary_data, file_extension = record
            
            # Add the extension to the path if it's missing
            if not output_path.endswith(file_extension):
                output_path += file_extension
            
            with open(output_path, 'wb') as file:
                file.write(binary_data)
            
            print(f"Image successfully saved to {output_path}")
            return True
        else:
            print(f"Image with ID {image_id} not found")
            return False
            
    except sqlite3.Error as e:
        print(f"Error working with the database: {e}")
        return False
    except IOError as e:
        print(f"Error writing file: {e}")
        return False
    finally:
        conn.close()

Getting Image Information Without Extraction

def get_image_info(image_id):
    try:
        conn = sqlite3.connect('images.db')
        cursor = conn.cursor()
        
        cursor.execute('''
            SELECT name, file_extension, file_size, upload_date 
            FROM images 
            WHERE id = ?
        ''', (image_id,))
        
        record = cursor.fetchone()
        
        if record:
            name, extension, size, upload_date = record
            info = {
                'name': name,
                'extension': extension,
                'size': size,
                'upload_date': upload_date
            }
            return info
        else:
            return None
            
    except sqlite3.Error as e:
        print(f"Error working with the database: {e}")
        return None
    finally:
        conn.close()

Extracting All Images

def retrieve_all_images(output_directory):
    if not os.path.exists(output_directory):
        os.makedirs(output_directory)
    
    try:
        conn = sqlite3.connect('images.db')
        cursor = conn.cursor()
        
        cursor.execute('SELECT id, name, file_extension, data FROM images')
        records = cursor.fetchall()
        
        for record in records:
            image_id, name, extension, binary_data = record
            filename = f"{name}_{image_id}{extension}"
            filepath = os.path.join(output_directory, filename)
            
            with open(filepath, 'wb') as file:
                file.write(binary_data)
        
        print(f"Extracted {len(records)} images to {output_directory}")
        
    except sqlite3.Error as e:
        print(f"Error working with the database: {e}")
    finally:
        conn.close()

Updating Images in the Database

Updating an Existing Image

def update_image(image_id, new_file_path):
    if not os.path.exists(new_file_path):
        print("New file does not exist")
        return False
    
    file_extension = Path(new_file_path).suffix.lower()
    file_size = os.path.getsize(new_file_path)
    binary_data = convert_to_binary(new_file_path)
    
    if binary_data is None:
        return False
    
    try:
        conn = sqlite3.connect('images.db')
        cursor = conn.cursor()
        
        # Check if the record exists
        cursor.execute('SELECT id FROM images WHERE id = ?', (image_id,))
        if not cursor.fetchone():
            print(f"Image with ID {image_id} not found")
            return False
        
        cursor.execute('''
            UPDATE images 
            SET data = ?, file_extension = ?, file_size = ?, upload_date = CURRENT_TIMESTAMP
            WHERE id = ?
        ''', (binary_data, file_extension, file_size, image_id))
        
        conn.commit()
        
        if cursor.rowcount > 0:
            print(f"Image with ID {image_id} successfully updated")
            return True
        else:
            print(f"Failed to update image with ID {image_id}")
            return False
            
    except sqlite3.Error as e:
        print(f"Error working with the database: {e}")
        return False
    finally:
        conn.close()

Updating Image Metadata

def update_image_metadata(image_id, new_name):
    try:
        conn = sqlite3.connect('images.db')
        cursor = conn.cursor()
        
        cursor.execute('''
            UPDATE images 
            SET name = ? 
            WHERE id = ?
        ''', (new_name, image_id))
        
        conn.commit()
        
        if cursor.rowcount > 0:
            print(f"Metadata for image with ID {image_id} updated")
            return True
        else:
            print(f"Image with ID {image_id} not found")
            return False
            
    except sqlite3.Error as e:
        print(f"Error working with the database: {e}")
        return False
    finally:
        conn.close()

Managing the Image Database

Deleting Images

def delete_image(image_id):
    try:
        conn = sqlite3.connect('images.db')
        cursor = conn.cursor()
        
        cursor.execute('DELETE FROM images WHERE id = ?', (image_id,))
        conn.commit()
        
        if cursor.rowcount > 0:
            print(f"Image with ID {image_id} deleted")
            return True
        else:
            print(f"Image with ID {image_id} not found")
            return False
            
    except sqlite3.Error as e:
        print(f"Error working with the database: {e}")
        return False
    finally:
        conn.close()

Getting a List of All Images

def list_all_images():
    try:
        conn = sqlite3.connect('images.db')
        cursor = conn.cursor()
        
        cursor.execute('''
            SELECT id, name, file_extension, file_size, upload_date 
            FROM images 
            ORDER BY upload_date DESC
        ''')
        
        records = cursor.fetchall()
        
        if records:
            print("List of images in the database:")
            print("-" * 80)
            for record in records:
                image_id, name, extension, size, upload_date = record
                size_kb = size / 1024
                print(f"ID: {image_id} | Name: {name} | Type: {extension} | Size: {size_kb:.2f} KB | Date: {upload_date}")
        else:
            print("Database contains no images")
            
        return records
        
    except sqlite3.Error as e:
        print(f"Error working with the database: {e}")
        return []
    finally:
        conn.close()

Clearing the Database

def clear_database():
    try:
        conn = sqlite3.connect('images.db')
        cursor = conn.cursor()
        
        cursor.execute('DELETE FROM images')
        conn.commit()
        
        print(f"Deleted {cursor.rowcount} images from the database")
        
    except sqlite3.Error as e:
        print(f"Error working with the database: {e}")
    finally:
        conn.close()

Optimizing Image Handling in SQLite

Compressing Images Before Saving

from PIL import Image
import io

def compress_image(file_path, quality=85, max_width=1920, max_height=1080):
    try:
        with Image.open(file_path) as img:
            # Resize if necessary
            if img.width > max_width or img.height > max_height:
                img.thumbnail((max_width, max_height), Image.Resampling.LANCZOS)
            
            # Save to buffer with compression
            buffer = io.BytesIO()
            img_format = img.format if img.format else 'JPEG'
            
            if img_format == 'JPEG':
                img.save(buffer, format=img_format, quality=quality, optimize=True)
            else:
                img.save(buffer, format=img_format)
            
            return buffer.getvalue()
            
    except Exception as e:
        print(f"Error compressing image: {e}")
        return None

Creating Indexes to Improve Performance

def create_indexes():
    try:
        conn = sqlite3.connect('images.db')
        cursor = conn.cursor()
        
        # Index for searching by name
        cursor.execute('CREATE INDEX IF NOT EXISTS idx_name ON images(name)')
        
        # Index for sorting by date
        cursor.execute('CREATE INDEX IF NOT EXISTS idx_upload_date ON images(upload_date)')
        
        # Index for filtering by size
        cursor.execute('CREATE INDEX IF NOT EXISTS idx_file_size ON images(file_size)')
        
        conn.commit()
        print("Indexes successfully created")
        
    except sqlite3.Error as e:
        print(f"Error creating indexes: {e}")
    finally:
        conn.close()

Alternative Approaches to Image Storage

Storing File Paths Instead of Images

def create_file_path_table():
    conn = sqlite3.connect('images_paths.db')
    cursor = conn.cursor()
    
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS image_paths (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            file_path TEXT NOT NULL,
            file_extension TEXT,
            file_size INTEGER,
            upload_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )
    ''')
    
    conn.commit()
    conn.close()
def insert_image_path(name, file_path):
    if not os.path.exists(file_path):
        print("File does not exist")
        return False
    
    file_extension = Path(file_path).suffix.lower()
    file_size = os.path.getsize(file_path)
    
    try:
        conn = sqlite3.connect('images_paths.db')
        cursor = conn.cursor()
        
        cursor.execute('''
            INSERT INTO image_paths (name, file_path, file_extension, file_size)
            VALUES (?, ?, ?, ?)
        ''', (name, file_path, file_extension, file_size))
        
        conn.commit()
        print(f"Path to image successfully saved")
        return True
        
    except sqlite3.Error as e:
        print(f"Error working with the database: {e}")
        return False
    finally:
        conn.close()

Hybrid Approach: Thumbnails in Database, Originals in File System

def create_hybrid_table():
    conn = sqlite3.connect('hybrid_images.db')
    cursor = conn.cursor()
    
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS hybrid_images (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            original_path TEXT NOT NULL,
            thumbnail BLOB,
            file_extension TEXT,
            file_size INTEGER,
            upload_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )
    ''')
    
    conn.commit()
    conn.close()
def create_thumbnail(image_path, size=(150, 150)):
    try:
        with Image.open(image_path) as img:
            img.thumbnail(size, Image.Resampling.LANCZOS)
            
            buffer = io.BytesIO()
            img_format = img.format if img.format else 'JPEG'
            img.save(buffer, format=img_format)
            
            return buffer.getvalue()
            
    except Exception as e:
        print(f"Error creating thumbnail: {e}")
        return None

Error Handling and Best Practices

Context Manager for Working with the Database

from contextlib import contextmanager

@contextmanager
def get_db_connection(db_path='images.db'):
    conn = None
    try:
        conn = sqlite3.connect(db_path)
        yield conn
    except sqlite3.Error as e:
        if conn:
            conn.rollback()
        print(f"Database error: {e}")
        raise
    finally:
        if conn:
            conn.close()
def safe_insert_image(name, file_path):
    binary_data = convert_to_binary(file_path)
    if binary_data is None:
        return False
    
    try:
        with get_db_connection() as conn:
            cursor = conn.cursor()
            cursor.execute('''
                INSERT INTO images (name, data)
                VALUES (?, ?)
            ''', (name, binary_data))
            conn.commit()
            return True
    except Exception as e:
        print(f"Failed to save image: {e}")
        return False

Image Validation

def is_valid_image(file_path):
    try:
        with Image.open(file_path) as img:
            img.verify()
        return True
    except Exception:
        return False
def get_image_metadata(file_path):
    try:
        with Image.open(file_path) as img:
            return {
                'format': img.format,
                'mode': img.mode,
                'size': img.size,
                'has_transparency': img.mode in ('RGBA', 'LA') or 'transparency' in img.info
            }
    except Exception as e:
        print(f"Error getting metadata: {e}")
        return None

Frequently Asked Questions

What is the maximum BLOB size in SQLite?

SQLite can theoretically store BLOBs up to 281 terabytes in size, but practical limitations depend on available memory and settings. It is recommended to limit the size of files to several megabytes for optimal performance.

How to ensure data integrity when working with images?

Use transactions to group operations, check for file existence before operations, apply data validation, and implement exception handling.

Can BLOB data be indexed in SQLite?

SQLite cannot create indexes directly for BLOB data. However, you can index related fields such as file name, size, or upload date.

How to optimize performance when working with a large number of images?

  • Use batch operations to insert multiple records.
  • Create indexes for frequently used search fields.
  • Consider compressing images before saving.
  • Use lazy loading for large datasets.

Is it safe to store images in SQLite for web applications?

For web applications with heavy traffic, it is recommended to store images in the file system or CDN, and only store file paths in the database. This will provide better performance and scalability.

Can damaged images be recovered from the database?

If the BLOB data was saved correctly, images can be recovered even if the original files are damaged. It is recommended to regularly back up the database.

Now you have a complete set of knowledge for effectively working with images in an SQLite database using Python. Apply the capabilities of the sqlite3 library, optimize data storage, and use the sqlite update python commands to manage images directly in the database.

News