cx_Freeze打包exe偶发sqlite3.OperationalError:无法打开数据库文件
解决cx_Freeze打包exe后sqlite3数据库偶尔无法打开的问题
核心问题分析
打包后exe运行时的当前工作目录不一定是exe所在目录,导致getcwd()获取的路径不可靠;同时打包时若将game.db纳入资源文件,可能被放到只读的临时解压目录,无法写入;权限不足或数据库锁定也会引发偶发错误。
具体解决方案
1. 修复数据库路径获取逻辑
替换依赖getcwd()的路径获取方式,改为获取exe的实际所在目录(打包前后均适用),并确保目录创建逻辑更健壮:
import sys import os import sqlite3 # 获取当前程序的根目录(开发/打包模式通用) if getattr(sys, 'frozen', False): # 打包为exe后的运行模式 root_dir = os.path.dirname(sys.executable) else: # 开发时直接运行py文件的模式 root_dir = os.path.dirname(os.path.abspath(__file__)) # 定义数据库目录和文件路径 db_dir = os.path.join(root_dir, "Database") db_path = os.path.join(db_dir, "game.db") # 先确保数据库目录存在 if not os.path.exists(db_dir): try: os.makedirs(db_dir) except OSError as e: raise RuntimeError(f"创建数据库目录失败: {str(e)}") # 连接数据库,添加超时参数避免锁定 try: connection = sqlite3.connect(db_path, timeout=10) connection.execute("PRAGMA foreign_keys = 1;") cursor = connection.cursor() except sqlite3.OperationalError as e: raise RuntimeError(f"打开数据库失败: {str(e)}") # 保持原有表初始化逻辑 def run(sql): cursor.execute(sql) connection.commit() return [i for i in cursor] run("CREATE TABLE IF NOT EXISTS accounts (playerName TEXT PRIMARY KEY, playerPassword TEXT);") run("CREATE TABLE IF NOT EXISTS scores (playerName REFERENCES accounts(playerName), difficulty TEXT, score INTEGER);")
2. 调整setup.py打包配置
移除include_files中的game.db,避免打包只读的数据库文件,让程序在运行时自动创建可写的数据库:
import cx_Freeze import pkgutil from os.path import join as path_join def get_all_packages(): return [i.name for i in list(pkgutil.iter_modules()) if i.ispkg] executables = [cx_Freeze.Executable('main.py', base="Win32GUI", target_name="Push-Ups Game", icon=path_join("Images", "Push-Ups Game Logo.ico"))] packages = ["collections", "encodings", "importlib", "pygame", "cv2", "mediapipe", "math", "sqlite3", "random", "numpy", "time", "os", "re", "ctypes", "matplotlib", "logging", "urllib", "packaging", "PIL", "pyparsing", "html", "cycler", "dateutil", "kiwisolver", "json", "xml", "http", "attr", "sys"] cx_Freeze.setup( name="Game", options={"build_exe": {"includes": packages, "excludes": [i for i in get_all_packages() if i not in packages], "include_files": ["Music/", "Images/"]}}, # 移除game.db executables=executables )
3. 解决权限与锁定问题
- 不要将exe放在系统保护目录(如
C:\Program Files),建议放到用户目录(如Documents、桌面)或自定义非系统目录,避免写入权限不足。 - 添加
timeout=10参数让sqlite3连接等待10秒再报错,减少多线程/进程场景下的数据库锁定偶发错误。
内容的提问来源于stack exchange,提问作者CozyCode
相关产品推荐
相关产品推荐

