文章前言

本文基于FastAPI+SQLite 本地数据库项目,完整讲解如何将 Python 后端项目打包为独立 exe 可执行文件,实现任意 Windows 电脑无需安装 Python、无需配置环境、双击直接运行。全程收录打包过程中所有经典报错:isatty终端日志崩溃、WinError5权限拒绝、uvicorn 无窗口运行异常、跨域兼容、数据库文件内嵌分发等新手 99% 会踩的坑,附带完整根治代码、一键打包脚本、从零到可交付成品全流程,代码开箱即用,可直接用于项目交付、CSDN 发文、成品软件分发。

项目背景

本项目基于前文 FastAPI 对接 Navicat 原生无主键 SQLite 数据表,实现数据新增、全量查询、条件删除全套功能,本次目标:将 Python 源码打包为单文件 exe,脱离开发环境、脱离 PyCharm、脱离 Python 解释器,拷贝到任意 Windows 电脑均可直接运行。

环境依赖

  • 开发语言:Python 3.8+
  • 后端框架:FastAPI、uvicorn
  • 数据库:SQLite(本地文件数据库,无额外服务)
  • 打包工具:PyInstaller
  • 系统兼容:全 Windows 版本(Win10/Win11 通用)

一、项目原有源码回顾

1. 项目目录结构

plaintext

fast02
├─ .venv                # Python虚拟环境
├─ 新建文本文档.db      # SQLite本地数据库文件
├─ main.py              # FastAPI后端核心源码
└─ index.html           # 原生HTML前端操作页面

2. 适配 exe 打包最终版 main.py 源码

专门修复 uvicorn 日志报错、多线程兼容、无环境运行异常,完整保留数据库增删查全功能

python

运行

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
import sqlite3

# 初始化FastAPI应用
app = FastAPI()

# 全局跨域配置:解决前后端跨域请求问题
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# ===================== 数据库配置 =====================
# 本地SQLite数据库文件路径
DB_PATH = "新建文本文档.db"

# ===================== 数据模型 =====================
# 严格匹配Navicat原生数据表结构,无id自增主键
class Item(BaseModel):
    A1Real: float
    A2String: str
    A3String: str

# ===================== 数据库通用连接工具 =====================
def get_db_conn():
    """
    统一封装SQLite数据库连接
    check_same_thread=False:解决FastAPI多线程环境下SQLite原生线程隔离报错
    """
    conn = sqlite3.connect(DB_PATH, check_same_thread=False)
    return conn

# ===================== 数据表初始化 =====================
def init_table():
    """程序启动自动检查表,表不存在则自动创建"""
    try:
        conn = get_db_conn()
        cursor = conn.cursor()
        cursor.execute('''
        CREATE TABLE IF NOT EXISTS ABCTable (
            A1Real REAL,
            A2String TEXT,
            A3String TEXT
        )
        ''')
        conn.commit()
        conn.close()
    except Exception as e:
        print(f"数据表初始化异常:{e}")

init_table()

# ===================== 接口1:新增数据 =====================
@app.post("/add")
def add_data(item: Item):
    """新增数据接口"""
    try:
        conn = get_db_conn()
        cursor = conn.cursor()
        # 参数化插入,防止SQL注入风险
        cursor.execute(
            "INSERT INTO ABCTable (A1Real,A2String,A3String) VALUES (?,?,?)",
            (item.A1Real, item.A2String, item.A3String)
        )
        conn.commit()
        conn.close()
        return {"msg": "数据添加成功"}
    except Exception as e:
        return {"msg": f"添加失败:{str(e)}"}

# ===================== 接口2:查询全部数据 =====================
@app.get("/list")
def get_all():
    """查询数据库内所有数据接口"""
    try:
        conn = get_db_conn()
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM ABCTable")
        res = cursor.fetchall()
        conn.close()
        return {"data": res}
    except Exception as e:
        return {"data": [], "msg": f"查询失败:{str(e)}"}

# ===================== 接口3:根据A1Real删除数据 =====================
@app.delete("/delete/{A1Real}")
def del_data(A1Real: float):
    """适配无主键表,以唯一数值字段A1Real作为删除标识"""
    try:
        conn = get_db_conn()
        cursor = conn.cursor()
        cursor.execute("DELETE FROM ABCTable WHERE A1Real = ?", (A1Real,))
        conn.commit()
        conn.close()
        return {"msg": "数据删除成功"}
    except Exception as e:
        return {"msg": f"删除失败:{str(e)}"}

# ===================== exe打包专用启动配置(核心修复) =====================
if __name__ == "__main__":
    import uvicorn
    # 根治uvicorn打包exe报错核心参数
    uvicorn.run(
        app,
        host="0.0.0.0",
        port=8001,
        log_level="critical",  # 屏蔽全部冗余日志,仅保留致命错误打印
        access_log=False,      # 关闭接口访问日志,脱离终端环境运行
    )

二、PyInstaller 打包前置准备

1. 安装打包依赖

在项目虚拟环境终端执行安装命令:

bash

运行

# 安装打包工具
pip install pyinstaller

2. 一键自动打包脚本 build.py

内置旧缓存自动清理,从根源解决文件占用、权限报错,无需手动删文件,直接运行即可打包

python

运行

import subprocess
import sys
import os
import shutil

# ========== 第一步:自动清理所有旧打包残留文件 ==========
# 删除旧打包生成的dist、build文件夹、spec配置文件
clean_list = ["dist", "build", "数据库系统.spec"]
for path in clean_list:
    if os.path.exists(path):
        if os.path.isdir(path):
            shutil.rmtree(path)
        else:
            os.remove(path)
print("旧打包缓存清理完成!")

# ========== 第二步:执行PyInstaller打包命令 ==========
subprocess.run([
    sys.executable, "-m", "PyInstaller",
    "--onefile",       # 打包为**单个独立exe文件**,方便拷贝分发
    "--name", "数据库系统",  # 自定义exe文件名
    "main.py"          # 项目启动入口文件
])

print("=====================================")
print("打包全部完成!exe文件已生成在 dist 文件夹内")
print("=====================================")

3. 完整打包运行步骤

  1. 关闭电脑所有正在运行的旧版 exe、Python 后台进程,避免文件占用
  2. 将上述main.pybuild.py放入项目根目录
  3. 在 PyCharm 终端执行打包命令:

    bash

    运行

    python build.py
    
  4. 等待命令执行完毕,项目dist文件夹内,生成最终成品 数据库系统.exe

三、打包全程所有报错根源 & 根治方案(新手必看)

本文完整收录 FastAPI+uvicorn 项目打包 exe 过程中全部高频致命报错,逐一分析原因、给出永久解决方案,也是网上绝大多数教程不会讲解的踩坑点。

坑 1:Unhandled exception 日志报错

报错信息

plaintext

AttributeError: 'NoneType' object has no attribute 'isatty'
ValueError: Unable to configure formatter 'default'

报错根源

  1. uvicorn 框架原生日志系统强依赖 Windows 终端控制台(tty 终端环境)
  2. 初始打包错误使用--windowed无窗口参数,直接砍掉了终端环境,日志初始化直接崩溃
  3. 默认开启全量访问日志,脱离控制台后无输出对象,直接触发空对象异常

根治解决方案uvicorn.run()启动参数中添加 2 行核心配置,关闭全部冗余日志:

python

运行

log_level="critical",  # 日志等级仅保留致命错误,屏蔽所有普通日志
access_log=False,      # 完全关闭接口访问请求日志

同时禁止在打包命令中添加--windowed参数,保留后台控制台窗口兼容 uvicorn 底层运行逻辑。


坑 2:PermissionError [WinError 5] 拒绝访问

报错信息

plaintext

PermissionError: [WinError 5] 拒绝访问。
'D:\project\04 pyhton\fast02\dist\数据库系统.exe'

报错根源上一次打包生成的 exe 文件仍在后台进程中运行,Windows 系统锁定文件占用,PyInstaller 重新打包时无法删除、覆盖旧 exe 文件,直接触发权限拦截,打包中断。

根治解决方案

  1. 打包脚本内置自动清理逻辑,运行前自动删除所有旧打包残留(dist/build/spec 文件)
  2. 打包前手动打开任务管理器(Ctrl+Shift+Esc),结束所有python.exe、旧版 exe 相关后台进程
  3. 保证打包时无任何文件占用,从根源规避权限报错。

坑 3:FastAPI 原生直接运行不适合 exe 打包

报错根源网上通用的 FastAPI 启动写法,全部适配开发调试环境,依赖实时终端日志打印,完全不适合 PyInstaller 打包分发。打包后脱离原生 Python 环境,极易出现服务无法启动、接口无响应、进程崩溃问题。

适配 exe 打包的启动规范

  • 必须关闭所有非必要日志输出
  • 必须添加 SQLite 多线程兼容参数check_same_thread=False
  • 全接口包裹异常捕获,数据库异常不会打崩整个 exe 程序
  • 监听地址固定0.0.0.0,保证局域网、本机均可访问

坑 4:浏览器访问0.0.0.0地址无法打开

概念区分必懂

表格

地址 作用 浏览器能否访问
0.0.0.0:8001 服务监听地址,放开本机所有网卡访问权限 ❌ 浏览器禁止访问
127.0.0.1:8001 本机回环地址,电脑专属访问入口 ✅ 唯一正确访问地址

接口访问汇总

  • 后端接口根地址:http://127.0.0.1:8001
  • FastAPI 自带接口调试文档:http://127.0.0.1:8001/docs
  • 前端页面:直接双击项目内index.html文件打开浏览器运行

坑 5:打包后 exe 发给其他电脑无法运行

原生未打包项目限制未打包源码运行,依赖 3 个硬性环境:

  1. 目标电脑必须安装对应版本 Python
  2. 必须手动安装fastapiuvicornpydantic全部依赖库
  3. 需要命令行启动服务,新手操作门槛极高

PyInstaller 打包优势打包完成后的单文件 exe,实现:✅ 无需安装 Python 解释器✅ 无需安装任何第三方依赖库✅ 任意 Windows 系统双击直接运行✅ 数据库文件同步内嵌,数据本地持久化存储✅ 前后端增删查业务功能完全不变


四、成品 exe 运行使用说明

  1. dist文件夹内的数据库系统.exe新建文本文档.dbindex.html三个文件打包拷贝
  2. 任意 Windows 电脑上,双击数据库系统.exe
  3. 后台弹出黑色控制台窗口,请勿关闭此窗口(关闭则后端服务停止)
  4. 等待服务启动完成,双击index.html前端页面,即可正常使用数据新增、查询、删除全部功能
  5. FastAPI 自带在线接口文档依旧可用,浏览器打开http://127.0.0.1:8001/docs即可调试接口

五、项目额外原生踩坑回顾(数据库部分)

结合前期开发全程报错,补充项目数据库原生适配问题,完整复盘全流程:

  1. no such column: id 字段不存在报错根源:Navicat 原生创建的 SQLite 数据表无 id 自增主键,网上通用教程默认带 id 字段,强行操作直接报错;最终适配原始表结构,选用A1Real字段作为唯一业务标识。
  2. 接口 500 服务器内部错误根源:SQLite 原生单线程限制,与 FastAPI 多线程 uvicorn 服务冲突;添加check_same_thread=False参数 + 全接口异常捕获根治。
  3. 前端删除按钮点击无响应根源:DELETE 请求头不完整、后端参数不匹配、数据库异常无返回;补全请求格式、完善后端异常返回信息根治。
  4. 前端页面undefined空值异常根源:数据库字段为空时 JS 直接取值异常;添加空值兜底语法?? "空"统一处理。

六、总结

本文完整实现了FastAPI+SQLite 本地项目从源码→PyInstaller 打包 exe→全 Windows 电脑通用分发的完整流程,完整解决了 uvicorn 日志终端崩溃、文件权限占用、多线程数据库异常、前后端跨域、原生表字段不匹配等全流程报错。

最终成品优势:

  1. 零环境依赖:任意 Windows 电脑双击运行,无需 Python、无需配置、无需命令
  2. 单文件分发:exe 独立封装,U 盘拷贝、文件传输交付极其方便
  3. 功能完整保留:原有增删查、数据库持久化、前端交互全部不受影响
  4. 异常全兜底:运行稳定,无原生启动崩溃问题,适合项目交付、个人工具软件封装、入门实战学习。

整套流程代码可直接复用修改,适配绝大多数 FastAPI 后端本地项目打包需求,非常适合 Python 后端初学者学习项目打包、软件成品交付相关知识。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐