From 15e6c7010369b52e1e333adfc72ebc22335be6f8 Mon Sep 17 00:00:00 2001 From: zzw <2029503428@qq.com> Date: Sat, 20 Jun 2026 21:12:52 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=93=E9=A2=98=E5=9B=BE=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E8=99=9A=E6=8B=9F=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/qgis_map_export.py | 10 +++--- app/services/qgis/qgis_env.py | 61 +++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/app/api/qgis_map_export.py b/app/api/qgis_map_export.py index ed67263..cfa733d 100644 --- a/app/api/qgis_map_export.py +++ b/app/api/qgis_map_export.py @@ -379,7 +379,7 @@ def _generate_batch_maps(models: list, config: dict, disaster_time: str) -> None import json import subprocess import tempfile - from app.services.qgis.qgis_env import build_qgis_command + from app.services.qgis.qgis_env import build_qgis_command, build_clean_subprocess_env try: logger.info(f"[批量产图] 开始: {len(models)} 张专题图, batch={disaster_time}") @@ -409,10 +409,10 @@ def _generate_batch_maps(models: list, config: dict, disaster_time: str) -> None cmd, capture_output=True, timeout=600, # 10 分钟超时(15 张模板 × ~40s/张 ≈ 600s) - # 不传 env —— 让子进程继承父进程环境, - # runner 内部 _setup_environment() 会设置 QGIS 所需变量。 - # build_qgis_env() 设置的 PYTHONPATH/PATH/QGIS_PREFIX_PATH - # 与 QGIS DLL 加载冲突,导致 0xC0000005 崩溃。 + env=build_clean_subprocess_env(), + # 使用干净的环境变量:移除 venv 的 PYTHONPATH/VIRTUAL_ENV/PATH 污染, + # 避免 QGIS Python 3.12 的 DLL 加载被干扰导致 0xC0000005 崩溃。 + # QGIS Python 3.12 仅通过 sys.path 即可正确加载所有模块和 DLL。 ) finally: try: diff --git a/app/services/qgis/qgis_env.py b/app/services/qgis/qgis_env.py index 530156a..8bb76e1 100644 --- a/app/services/qgis/qgis_env.py +++ b/app/services/qgis/qgis_env.py @@ -160,6 +160,67 @@ def build_qgis_env(qgis_root: str = None) -> dict: return env +def build_clean_subprocess_env() -> dict: + """ + 为 QGIS Python 3.12 子进程构建干净的环境变量。 + + 问题背景: + 主进程运行在 venv Python 3.10 中,继承了 venv 的环境变量 + (PYTHONPATH 指向 venv site-packages、VIRTUAL_ENV、PATH 含 venv Scripts 等)。 + QGIS Python 3.12 子进程如果继承这些变量,DLL 加载会被干扰, + 导致 0xC0000005 (ACCESS_VIOLATION) 崩溃。 + + 同时,QGIS_PREFIX_PATH、QT_PLUGIN_PATH 等变量也会与 + QGIS 内置 DLL 加载机制冲突,同样导致崩溃。 + + 策略: + 从 os.environ 中移除所有 Python/venv 相关的污染变量, + 只保留操作系统正常运行所需的基础变量(SystemRoot、TEMP 等)。 + QGIS Python 3.12 仅通过 sys.path 即可正确加载所有模块和 DLL。 + """ + env = dict(os.environ) + + # 先记录 venv 路径(后续清理 PATH 时需要用) + venv_root = env.get("VIRTUAL_ENV", "").lower() + + # 移除 Python/venv 相关变量 —— 这些会污染 QGIS Python 3.12 的 DLL 加载 + for key in [ + "PYTHONPATH", # venv 设置,指向 site-packages → DLL 冲突 + "PYTHONHOME", # 如果存在,改变 Python 查找路径 + "VIRTUAL_ENV", # venv 标识,不必要 + "PYTHONDONTWRITEBYTECODE", + "QGIS_PREFIX_PATH", # 与 QGIS 内置 DLL 加载冲突 + "QT_PLUGIN_PATH", # 与 Qt DLL 加载冲突 + "GDAL_DATA", # build_qgis_env 设置,可能导致路径冲突 + "PROJ_DATA", # build_qgis_env 设置,可能导致路径冲突 + ]: + env.pop(key, None) + + # 清理 PATH:仅移除 venv 相关路径(避免 DLL 搜索顺序冲突) + # 保留 QGIS/系统路径(测试证明这些是安全的) + if venv_root: + path_parts = env.get("PATH", "").split(";") + clean_parts = [] + for p in path_parts: + pl = p.lower().strip() + if not pl: + continue + # 跳过 venv 相关路径(.venv/Scripts, .venv/Lib 等) + if venv_root in pl: + continue + clean_parts.append(p) + env["PATH"] = ";".join(clean_parts) + + # 移除 VIRTUAL_ENV 本身(已从 PATH 清理中使用过) + env.pop("VIRTUAL_ENV", None) + + logger.debug( + f"Clean env built: removed PYTHONPATH/VIRTUAL_ENV/venv-PATH, " + f"kept {len(env)} vars" + ) + return env + + def is_qgis_available(qgis_root: str = None) -> bool: """检查 QGIS 环境是否可用""" return get_qgis_python_path(qgis_root) is not None