diff --git a/QGIS_DOCKER_README.md b/QGIS_DOCKER_README.md index 73905e0..d6e9ff5 100644 --- a/QGIS_DOCKER_README.md +++ b/QGIS_DOCKER_README.md @@ -32,15 +32,11 @@ docker load -i qgis-34411.tar ```bash # ---- Linux / macOS ---- - -PROJECT_ROOT=/home/xian/xian_algorithm_new -FILE_STORE=/data - docker run -d \ --name qgis-server \ --restart unless-stopped \ - -v "${PROJECT_ROOT}:/app:ro" \ - -v "${FILE_STORE}:${FILE_STORE}" \ + -v "/www/wwwroot/xian_algorithm_new:/app:ro" \ + -v "/www/wwwroot/xian_algorithm_new/files:/files" \ qgis/qgis:3.44.11 \ sleep infinity @@ -124,60 +120,103 @@ docker exec qgis-server ls -la /data/template/earthquake - 容器重建后(`/data` 目录丢失) - 首次部署时 -## 5. 安装中文字体(手动) +## 5. 安装中文字体(手动,必须执行) QGIS 模板使用了 SimHei(黑体)、SimSun(宋体)、Microsoft YaHei(微软雅黑)等 Windows 中文字体, Docker 镜像默认不包含这些字体,会导致中文全部乱码。**字体需手动安装,代码不会自动安装。** -### 步骤 +### 5.1 准备字体文件 + +字体文件存放在项目根目录的 `fonts/` 目录下,已预置 4 个常用中文字体: + +``` +fonts/ +├── simhei.ttf — 黑体(模板默认字体) +├── simsun.ttc — 宋体 +├── msyh.ttc — 微软雅黑 +└── msyhbd.ttc — 微软雅黑粗体 +``` + +如果字体缺失,从 Windows 主机复制(`C:\Windows\Fonts\`): ```bash -# 1. 创建字体目录 -docker exec qgis-server mkdir -p /usr/share/fonts/truetype/winfonts +# Linux 服务器用 SCP 从 Windows 传 +scp "C:\Windows\Fonts\simhei.ttf" root@服务器IP:/www/wwwroot/xian_algorithm_new/fonts/ +scp "C:\Windows\Fonts\simsun.ttc" root@服务器IP:/www/wwwroot/xian_algorithm_new/fonts/ +scp "C:\Windows\Fonts\msyh.ttc" root@服务器IP:/www/wwwroot/xian_algorithm_new/fonts/ +scp "C:\Windows\Fonts\msyhbd.ttc" root@服务器IP:/www/wwwroot/xian_algorithm_new/fonts/ +``` -# 2. 从主机复制 Windows 中文字体 -# Windows 路径(根据实际字体文件位置调整): -docker cp "C:\Windows\Fonts\simhei.ttf" qgis-server:/usr/share/fonts/truetype/winfonts/ -docker cp "C:\Windows\Fonts\simsun.ttc" qgis-server:/usr/share/fonts/truetype/winfonts/ -docker cp "C:\Windows\Fonts\msyh.ttc" qgis-server:/usr/share/fonts/truetype/winfonts/ -docker cp "C:\Windows\Fonts\msyhbd.ttc" qgis-server:/usr/share/fonts/truetype/winfonts/ +### 5.2 一键安装到容器 -# Linux 字体路径示例: -# docker cp /usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf qgis-server:/usr/share/fonts/truetype/winfonts/ +```bash +python app/script/install_fonts_to_container.py +``` -# 3. 刷新字体缓存 +输出示例: +``` +=== 安装中文字体到 Docker 容器 qgis-server === + + 字体目录: /www/wwwroot/xian_algorithm_new/fonts + 字体文件: 4 个 + - msyh.ttc (4165 KB) + - msyhbd.ttc (4167 KB) + - simhei.ttf (2637 KB) + - simsun.ttc (10126 KB) + 容器目标: qgis-server:/usr/share/fonts/truetype/winfonts + + [1/3] 复制字体文件... + OK msyh.ttc + OK msyhbd.ttc + OK simhei.ttf + OK simsun.ttc + + [2/3] 刷新字体缓存... + OK + + [3/3] 验证字体... + 中文字体: ['SimHei', 'SimSun', 'Microsoft YaHei', 'Microsoft YaHei UI'] + +=== 完成,耗时 3.2s === +``` + +其他用法: +```bash +python app/script/install_fonts_to_container.py --dry-run # 仅查看信息 +python app/script/install_fonts_to_container.py --container my # 指定容器名 +``` + +### 5.3 持久化(推荐) + +容器重建后字体丢失,**强烈建议挂载 `fonts/` 目录**。 + +```bash +# 停掉旧容器 +docker stop qgis-server && docker rm qgis-server + +# 重建容器,加上字体挂载 +docker run -d \ + --name qgis-server \ + --restart unless-stopped \ + -v "/www/wwwroot/xian_algorithm_new:/app:ro" \ + -v "/www/wwwroot/xian_algorithm_new/files:/files" \ + -v "/www/wwwroot/xian_algorithm_new/fonts:/usr/share/fonts/truetype/winfonts:ro" \ + qgis/qgis:3.44.11 \ + sleep infinity + +# 挂载后只需刷新一次缓存 docker exec qgis-server fc-cache -fv - -# 4. 验证字体已识别 -docker exec qgis-server python3 -c " -from PyQt5.QtGui import QFontDatabase -db = QFontDatabase() -zh = [f for f in db.families() if 'SimHei' in f or 'YaHei' in f or 'SimSun' in f] -print('中文字体:', zh) -" ``` -### 持久化方案 +### 5.4 无法获取 Windows 字体时的替代方案 -容器重建后字体丢失。可选方案: - -**方案 A:挂载单个字体文件** ```bash -docker run -d ... \ - -v "C:\Windows\Fonts\simhei.ttf:/usr/share/fonts/truetype/winfonts/simhei.ttf" \ - -v "C:\Windows\Fonts\simsun.ttc:/usr/share/fonts/truetype/winfonts/simsun.ttc" \ - ... -``` +# Linux 服务器安装开源中文字体 +yum install wqy-microhei-fonts # CentOS / RHEL +apt install fonts-wqy-microhei # Debian / Ubuntu -**方案 B:挂载整个字体目录(推荐)** -```bash -# 先在主机创建字体目录,放入所需字体文件 -mkdir -p /opt/qgis-fonts -cp /usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf /opt/qgis-fonts/ - -docker run -d ... \ - -v "/opt/qgis-fonts:/usr/share/fonts/truetype/winfonts:ro" \ - ... +# 将系统字体复制到项目 fonts/ 目录 +cp /usr/share/fonts/wqy-microhei/wqy-microhei.ttc /www/wwwroot/xian_algorithm_new/fonts/ ``` ## 6. 验证容器 diff --git a/app/core/server.py b/app/core/server.py index 31fb83f..1ff868e 100644 --- a/app/core/server.py +++ b/app/core/server.py @@ -56,6 +56,9 @@ def create_app() -> FastAPI: start = time.time() response = await call_next(request) elapsed = time.time() - start + # 静默健康检查探针 + if request.url.path == "/" and request.method == "GET": + return response logger.info(f"{request.method} {request.url.path} -> {response.status_code} ({elapsed:.3f}s)") return response diff --git a/app/script/install_fonts_to_container.py b/app/script/install_fonts_to_container.py new file mode 100644 index 0000000..0e9714a --- /dev/null +++ b/app/script/install_fonts_to_container.py @@ -0,0 +1,159 @@ +""" +将项目 fonts/ 目录下的中文字体安装到 Docker QGIS 容器。 + +QGIS 官方 Docker 镜像不包含中文字体,模板中的 SimHei/SimSun/YaHei 字体会显示为方块。 +本脚本将 fonts/ 目录下的字体文件复制到容器内并刷新字体缓存。 + +用法: + python app/script/install_fonts_to_container.py [--container qgis-server] [--dry-run] + +前置条件: + - Docker 容器已启动(docker start qgis-server) + - 项目根目录下 fonts/ 目录包含所需的 .ttf/.ttc 字体文件 +""" +import argparse +import subprocess +import sys +import time +from pathlib import Path + +PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent +FONTS_DIR = PROJECT_ROOT / "fonts" + +# 容器内字体目录 +CONTAINER_FONT_DIR = "/usr/share/fonts/truetype/winfonts" + + +def _run(cmd, timeout=10, **kwargs): + """subprocess.run 封装,统一 UTF-8 编码""" + return subprocess.run( + cmd, capture_output=True, timeout=timeout, + encoding="utf-8", errors="replace", **kwargs, + ) + + +def _load_config(): + """从 settings.toml 读取配置""" + try: + from config import settings + return { + "container": getattr(settings, "QGIS_DOCKER_CONTAINER", "qgis-server"), + } + except ImportError: + pass + + cfg = {"container": "qgis-server"} + toml_path = PROJECT_ROOT / "settings.toml" + if toml_path.exists(): + for line in toml_path.read_text(encoding="utf-8").splitlines(): + line = line.strip() + if line.startswith("QGIS_DOCKER_CONTAINER") and "=" in line: + cfg["container"] = line.split("=", 1)[1].strip().strip('"').strip("'") + return cfg + + +def _check_container(container: str): + """检查容器是否运行""" + result = _run(["docker", "inspect", "--format={{.State.Running}}", container], timeout=5) + if (result.stdout or "").strip() != "true": + raise RuntimeError(f"容器 {container} 未运行,请先 docker start {container}") + + +def install_fonts(container: str = None, dry_run: bool = False): + """将 fonts/ 目录下的字体安装到容器""" + cfg = _load_config() + if container is None: + container = cfg["container"] + + _check_container(container) + + # 扫描字体文件 + if not FONTS_DIR.is_dir(): + print(f"字体目录不存在: {FONTS_DIR}") + print(f"请先在项目根目录下创建 fonts/ 目录,并放入 .ttf/.ttc 字体文件。") + print(f"Windows 字体路径: C:\\Windows\\Fonts\\") + print(f" simhei.ttf — 黑体(模板默认字体)") + print(f" simsun.ttc — 宋体") + print(f" msyh.ttc — 微软雅黑") + print(f" msyhbd.ttc — 微软雅黑粗体") + sys.exit(1) + + font_files = [f for f in FONTS_DIR.iterdir() + if f.is_file() and f.suffix.lower() in (".ttf", ".ttc", ".otf")] + + if not font_files: + print(f"字体目录为空或无字体文件: {FONTS_DIR}") + print(f"请放入 .ttf/.ttc/.otf 字体文件后重试。") + sys.exit(1) + + print(f"=== 安装中文字体到 Docker 容器 {container} ===\n") + print(f" 字体目录: {FONTS_DIR}") + print(f" 字体文件: {len(font_files)} 个") + for f in sorted(font_files): + size_kb = f.stat().st_size / 1024 + print(f" - {f.name} ({size_kb:.0f} KB)") + print(f" 容器目标: {container}:{CONTAINER_FONT_DIR}") + print() + + if dry_run: + print(" [dry-run] 跳过安装") + return + + # 1. 在容器内创建字体目录 + t0 = time.time() + _run(["docker", "exec", container, "mkdir", "-p", CONTAINER_FONT_DIR]) + + # 2. 逐个复制字体文件到容器 + print(" [1/3] 复制字体文件...") + for f in sorted(font_files): + result = _run( + ["docker", "cp", str(f), f"{container}:{CONTAINER_FONT_DIR}/{f.name}"], + timeout=30, + ) + if result.returncode != 0: + print(f" FAIL {f.name}: {result.stderr.strip()}") + else: + print(f" OK {f.name}") + + # 3. 刷新字体缓存 + print("\n [2/3] 刷新字体缓存...") + result = _run(["docker", "exec", container, "fc-cache", "-fv"], timeout=30) + if result.returncode != 0: + print(f" WARN fc-cache 输出: {result.stderr.strip()}") + else: + print(" OK") + + # 4. 验证字体 + print("\n [3/3] 验证字体...") + verify_script = ( + "from PyQt5.QtGui import QFontDatabase; " + "db = QFontDatabase(); " + "zh = [f for f in db.families() if any(k in f for k in " + "['SimHei','YaHei','SimSun','WenQuanYi','Noto Sans CJK'])]; " + "print('中文字体:', zh if zh else '未安装!')" + ) + result = _run( + ["docker", "exec", container, "python3", "-c", verify_script], + timeout=10, + ) + print(f" {(result.stdout or '').strip()}") + + elapsed = time.time() - t0 + print(f"\n=== 完成,耗时 {elapsed:.1f}s ===") + + +def main(): + parser = argparse.ArgumentParser(description="将中文字体安装到 Docker QGIS 容器") + parser.add_argument("--container", default=None, help="Docker 容器名称") + parser.add_argument("--dry-run", action="store_true", help="仅显示信息,不实际安装") + args = parser.parse_args() + + try: + install_fonts(container=args.container, dry_run=args.dry_run) + except Exception as e: + print(f"错误: {e}", file=sys.stderr) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/fonts/msyh.ttc b/fonts/msyh.ttc new file mode 100644 index 0000000..ea174b2 Binary files /dev/null and b/fonts/msyh.ttc differ diff --git a/fonts/msyhbd.ttc b/fonts/msyhbd.ttc new file mode 100644 index 0000000..ac4e4a6 Binary files /dev/null and b/fonts/msyhbd.ttc differ diff --git a/fonts/simhei.ttf b/fonts/simhei.ttf new file mode 100644 index 0000000..1f1bdf8 Binary files /dev/null and b/fonts/simhei.ttf differ diff --git a/fonts/simsun.ttc b/fonts/simsun.ttc new file mode 100644 index 0000000..1203783 Binary files /dev/null and b/fonts/simsun.ttc differ diff --git a/xian_algorithm_new.zip b/xian_algorithm_new.zip new file mode 100644 index 0000000..b0f415e Binary files /dev/null and b/xian_algorithm_new.zip differ