【第6章 数据存储与网络】6.1 SQLite/JSON本地存储

以下是为统信UOS设计的Kivy应用本地存储完整解决方案,包含SQLite和JSON两种方式的详细实现:


统信UOS Kivy本地存储方案

一、存储路径规划(符合UOS规范)

1. 应用数据目录结构

~/.local/share/yourapp/  # 统信UOS推荐的应用数据位置
├── databases/          # SQLite数据库
│   └── app_data.db
├── json/               # JSON存储
│   ├── config.json
│   └── userdata.json
└── cache/              # 临时缓存

2. 路径获取方法

from kivy.app import App
import os
import platform

def get_storage_path():
    """获取符合统信UOS规范的存储路径"""
    app = App.get_running_app()
    
    # 统信UOS特殊处理
    if platform.system() == 'Linux' and 'UOS' in os.uname().version:
        base = os.path.expanduser('~/.local/share')
    else:
        base = app.user_data_dir
    
    paths = {
        'db': os.path.join(base, app.name, 'databases'),
        'json': os.path.join(base, app.name, 'json'),
        'cache': os.path.join(base, app.name, 'cache')
    }
    
    # 创建目录
    for path in paths.values():
        os.makedirs(path, exist_ok=True)
    
    return paths

二、SQLite数据库集成

1. 数据库初始化(带UOS兼容性处理)

import sqlite3
from kivy.clock import Clock

class DatabaseManager:
    def __init__(self):
        self.paths = get_storage_path()
        self.db_path = os.path.join(self.paths['db'], 'app_data.db')
        self._init_db()
        
    def _init_db(self):
        """初始化数据库表结构"""
        def _actual_init(dt):
            with self.get_connection() as conn:
                cursor = conn.cursor()
                cursor.execute('''
                    CREATE TABLE IF NOT EXISTS users (
                        id INTEGER PRIMARY KEY AUTOINCREMENT,
                        username TEXT UNIQUE NOT NULL,
                        password_hash TEXT,
                        last_login TIMESTAMP
                    )
                ''')
                cursor.execute('''
                    CREATE TABLE IF NOT EXISTS settings (
                        key TEXT PRIMARY KEY,
                        value TEXT
                    )
                ''')
                conn.commit()
        
        # 统信UOS下需要延迟执行
        Clock.schedule_once(_actual_init, 0)

    def get_connection(self):
        """获取数据库连接(带UOS文件锁处理)"""
        conn = sqlite3.connect(self.db_path, timeout=30)
        conn.execute('PRAGMA journal_mode=WAL')  # 统信UOS建议的日志模式
        return conn

2. 增删改查封装

class DatabaseManager:
    # ...接上...
    
    def add_user(self, username, password_hash):
        """添加用户"""
        try:
            with self.get_connection() as conn:
                conn.execute(
                    'INSERT INTO users (username, password_hash) VALUES (?, ?)',
                    (username, password_hash)
                )
                return True
        except sqlite3.IntegrityError:
            return False

    def get_setting(self, key, default=None):
        """获取设置项"""
        with self.get_connection() as conn:
            cursor = conn.cursor()
            cursor.execute('SELECT value FROM settings WHERE key = ?', (key,))
            result = cursor.fetchone()
            return result[0] if result else default

    def update_setting(self, key, value):
        """更新设置项"""
        with self.get_connection() as conn:
            conn.execute(
                '''INSERT OR REPLACE INTO settings (key, value) 
                VALUES (?, ?)''',
                (key, value)
            )

3. KV界面绑定示例

<LoginForm>:
    username: username_input
    password: password_input
    
    BoxLayout:
        orientation: 'vertical'
        TextInput:
            id: username_input
            hint_text: "用户名"
        TextInput:
            id: password_input
            hint_text: "密码"
            password: True
        Button:
            text: "登录"
            on_press: app.db_manager.login(root.username.text, root.password.text)

三、JSON存储方案

1. JSON管理器实现

import json
from threading import Lock
from kivy.utils import platform

class JSONStorage:
    _instance = None
    _lock = Lock()
    
    def __new__(cls):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
                    cls._instance._init()
        return cls._instance
    
    def _init(self):
        self.paths = get_storage_path()
        # 统信UOS下需要更严格的文件权限
        self.umask = 0o077 if platform == 'linux' and 'UOS' in os.uname().version else None
        
    def _safe_write(self, path, data):
        """安全的原子化写入(兼容UOS文件系统)"""
        temp_path = f"{path}.tmp"
        with open(temp_path, 'w', encoding='utf-8') as f:
            if self.umask is not None:
                old_mask = os.umask(self.umask)
            json.dump(data, f, ensure_ascii=False, indent=2)
            if self.umask is not None:
                os.umask(old_mask)
        os.replace(temp_path, path)
    
    def save_config(self, config_data):
        """保存配置"""
        path = os.path.join(self.paths['json'], 'config.json')
        self._safe_write(path, config_data)
    
    def load_config(self):
        """加载配置"""
        path = os.path.join(self.paths['json'], 'config.json')
        try:
            with open(path, 'r', encoding='utf-8') as f:
                return json.load(f)
        except (FileNotFoundError, json.JSONDecodeError):
            return {}

2. 配置自动保存装饰器

def autosave_json(func):
    """自动保存JSON的装饰器"""
    def wrapper(self, *args, **kwargs):
        result = func(self, *args, **kwargs)
        self._save_to_disk()
        return result
    return wrapper

class AppConfig:
    def __init__(self):
        self.data = JSONStorage().load_config()
    
    @autosave_json
    def set_theme(self, theme_name):
        self.data['theme'] = theme_name
    
    @autosave_json
    def set_language(self, lang_code):
        self.data['language'] = lang_code
    
    def _save_to_disk(self):
        JSONStorage().save_config(self.data)

四、数据加密(符合UOS安全规范)

1. 使用统信UOS密钥环

from cryptography.fernet import Fernet
import keyring

class DataEncryptor:
    def __init__(self, service_name):
        self.service = service_name
        self._setup_key()
    
    def _setup_key(self):
        """从统信UOS密钥环获取加密密钥"""
        try:
            key = keyring.get_password(self.service, 'fernet_key')
            if not key:
                key = Fernet.generate_key().decode()
                keyring.set_password(self.service, 'fernet_key', key)
            self.cipher = Fernet(key.encode())
        except Exception as e:
            print(f"密钥环访问失败,使用临时密钥: {e}")
            self.cipher = Fernet.generate_key()
    
    def encrypt(self, data: str) -> bytes:
        """加密数据"""
        return self.cipher.encrypt(data.encode())
    
    def decrypt(self, token: bytes) -> str:
        """解密数据"""
        return self.cipher.decrypt(token).decode()

2. 安全存储实现

class SecureStorage:
    def __init__(self):
        self.encryptor = DataEncryptor("com.yourapp.storage")
        self.db_manager = DatabaseManager()
    
    def save_credential(self, username, password):
        """安全存储凭据"""
        encrypted = self.encryptor.encrypt(password)
        self.db_manager.update_setting(
            f"cred_{username}",
            encrypted.hex()  # 存储为十六进制字符串
        )
    
    def get_credential(self, username):
        """获取解密后的凭据"""
        encrypted_hex = self.db_manager.get_setting(f"cred_{username}")
        if encrypted_hex:
            return self.encryptor.decrypt(bytes.fromhex(encrypted_hex))
        return None

五、完整示例:笔记应用

1. 数据库模型

class NoteDatabase(DatabaseManager):
    def __init__(self):
        super().__init__()
        
    def create_note(self, title, content, tags=""):
        with self.get_connection() as conn:
            cursor = conn.cursor()
            cursor.execute('''
                INSERT INTO notes (title, content, tags, created_at)
                VALUES (?, ?, ?, datetime('now'))
            ''', (title, content, tags))
            return cursor.lastrowid
    
    def get_notes(self, search_term=None):
        with self.get_connection() as conn:
            cursor = conn.cursor()
            if search_term:
                cursor.execute('''
                    SELECT * FROM notes 
                    WHERE title LIKE ? OR content LIKE ?
                    ORDER BY created_at DESC
                ''', (f"%{search_term}%", f"%{search_term}%"))
            else:
                cursor.execute('SELECT * FROM notes ORDER BY created_at DESC')
            return cursor.fetchall()

2. KV界面实现

<NoteEditor>:
    title: title_input
    content: content_input
    
    BoxLayout:
        orientation: 'vertical'
        TextInput:
            id: title_input
            hint_text: "标题"
            size_hint_y: None
            height: '50sp'
        
        ScrollView:
            TextInput:
                id: content_input
                hint_text: "内容"
                multiline: True
                padding: [10, 10]
        
        BoxLayout:
            size_hint_y: None
            height: '40sp'
            Button:
                text: "保存"
                on_press: app.save_note(root.title.text, root.content.text)
            Button:
                text: "加载"
                on_press: app.load_notes()

3. JSON配置集成

class NoteApp(App):
    def build(self):
        self.db = NoteDatabase()
        self.config = AppConfig()
        return NoteEditor()
    
    def save_note(self, title, content):
        note_id = self.db.create_note(title, content)
        recent = self.config.data.get('recent_notes', [])
        recent.append(note_id)
        self.config.set('recent_notes', recent[-10:])  # 只保留最近10条
    
    def on_start(self):
        # 应用启动时加载主题
        theme = self.config.get('theme', 'light')
        self.set_theme(theme)

六、统信UOS特殊优化

1. 数据库备份机制

def backup_database():
    """统信UOS下的定时备份"""
    paths = get_storage_path()
    db_path = os.path.join(paths['db'], 'app_data.db')
    backup_dir = os.path.join(paths['db'], 'backups')
    
    os.makedirs(backup_dir, exist_ok=True)
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    backup_path = os.path.join(backup_dir, f'backup_{timestamp}.db')
    
    # 使用统信UOS的rsync工具进行高效备份
    if platform == 'linux' and 'UOS' in os.uname().version:
        os.system(f'rsync -ah --progress {db_path} {backup_path}')
    else:
        shutil.copy2(db_path, backup_path)
    
    # 清理旧备份(保留最近7天)
    for f in os.listdir(backup_dir):
        if f.startswith('backup_') and f.endswith('.db'):
            path = os.path.join(backup_dir, f)
            if os.stat(path).st_mtime < time.time() - 7*86400:
                os.remove(path)

2. 性能优化建议

  1. SQLite调优

    conn.execute("PRAGMA synchronous = NORMAL")  # 统信UOS推荐设置
    conn.execute("PRAGMA cache_size = -10000")   # 10MB缓存
    
  2. JSON大数据处理

    # 使用ijson流式处理大JSON文件
    import ijson
    def parse_large_json(path):
        with open(path, "rb") as f:
            for item in ijson.items(f, "item"):
                process_item(item)
    
  3. 统信UOS文件监控

    from watchdog.observers.inotify import InotifyObserver
    
    def watch_config_changes():
        observer = InotifyObserver()
        observer.schedule(
            ConfigHandler(), 
            path=get_storage_path()['json'],
            recursive=False
        )
        observer.start()
    

关键问题解决方案

  1. 统信UOS权限问题

    # 在应用.desktop文件中声明需要的数据目录
    X-UOS-Data-Path=~/.local/share/yourapp
    
  2. 数据库锁定问题

    # 使用WAL模式+重试机制
    def get_connection_with_retry(max_retries=3):
        for i in range(max_retries):
            try:
                return sqlite3.connect(db_path, timeout=10)
            except sqlite3.OperationalError:
                if i == max_retries - 1:
                    raise
                time.sleep(0.1)
    
  3. 中文路径处理

    # 统信UOS下确保使用UTF-8编码
    path = path.encode('utf-8').decode('utf-8')
    with open(path, 'w', encoding='utf-8') as f:
        json.dump(data, f, ensure_ascii=False)
    

通过以上实现,您的Kivy应用可以在统信UOS上实现安全可靠的本地数据存储,同时兼顾性能和用户体验。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Botiway

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值