初步完成框架
Some checks failed
Android CI / build (push) Has been cancelled

- 实时图传:WebSocket JPEG 帧发送 + 帧率控制 + PC 浏览器预览
- PDF 上传与处理:上传/处理分离,支持 ocrpdf 和 markdown 两种类型
- MinerU 真实接入:markdown 处理 + images ZIP 打包
- OCRmyPDF 接入:ocrpdf 生成可搜索双层 PDF
- 手机端任务管理面板:轮询状态 + SAF 目录选择下载
- PC 管理面板:/dashboard 文件与任务管理
- 网络层:OkHttp 客户端、WebSocket 图传、局域网发现占位

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
MobKBK
2026-06-04 17:03:18 +08:00
parent dd8002009d
commit 1848a88fcf
72 changed files with 6281 additions and 163 deletions

View File

@@ -0,0 +1,392 @@
# MinerU 接入 FairScan PC Server 对接文档
> 本文档记录 MinerU 在本机的环境信息、API 用法,以及如何将其接入 FairScan PC 服务器,
> 替换当前的模拟处理逻辑。
---
## 1. 本机环境信息
> **统一环境**MinerU 和 OCRmyPDF 共用一个 conda 环境 `MinerU`
> PC 服务器始终通过 `conda activate MinerU` 启动。
| 项目 | 值 |
|------|-----|
| MinerU 源码路径 | `F:/datasets_rm/MinerU/` |
| **已安装版本** | **3.0.9** |
| **最新版本** | **3.2.2**446 commits 差距) |
| Conda 环境 | `D:/ProgramData/miniconda3/envs/MinerU/` |
| Python | 3.10.20 |
| PyTorch | 2.6.0+cu124 |
| CUDA | 12.4 |
| GPU | NVIDIA GeForce RTX 4060 Laptop GPU (8 GB VRAM) |
| Transformers | 4.57.6 |
| onnxruntime | 1.23.2 |
| Pipeline 模型 | ✅ 已下载HF cache: `C:/Users/32892/.cache/huggingface/hub/models--opendatalab--PDF-Extract-Kit-1.0` |
| VLM 模型 | ✅ 已下载HF cache: `C:/Users/32892/.cache/huggingface/hub/models--opendatalab--MinerU2.5-2509-1.2B` |
| HF Hub 离线模式 | ✅ `HF_HUB_OFFLINE=1`main.py 启动时设置) |
| OCRmyPDF | ✅ v15.4.4 已安装(源码 `F:/datasets_rm/ocRmypdf`,同一 conda 环境) |
| Tesseract | ❌ 待安装OCRmyPDF 必需依赖) |
---
## 2. 前置准备:升级 MinerU强烈建议
当前安装的 3.0.9 与最新 3.2.2 差距较大446 commits主要改进包括
- **`aio_do_parse()` 异步接口** — 可直接 await 调用,不阻塞 FastAPI 事件循环
- **并发锁优化** — Layout/MFR/OCR 使用独立推理锁,减少 GPU 争用
- **PDF 渲染修复** — 大量 PDFium 资源泄漏和崩溃修复
- **图像分析** — 新增 `image_analysis` 参数
- **Client-side 输出生成** — 新增 `client_side_output_generation` 选项
### 2.1 拉取最新代码
```bash
cd F:/datasets_rm/MinerU
git checkout main
git pull origin main
git checkout mineru-3.2.2-released
```
### 2.2 更新安装
```bash
conda activate MinerU
pip install -e .
```
### 2.3 验证
```bash
# 检查版本
python -c "from mineru.version import __version__; print(__version__)" # 应为 3.2.2
# 验证模型可用
python -c "
from mineru.utils.models_download_utils import auto_download_and_get_model_root_path
print('Pipeline:', auto_download_and_get_model_root_path('models/README.md', 'pipeline'))
print('VLM:', auto_download_and_get_model_root_path('/', 'vlm'))
"
```
> **注意**:如果之后需要用 `model_source=local` 指定自定义模型路径,才需要创建 `~/.mineru.json` 配置文件。默认的 HuggingFace 缓存模式不需要。
---
## 3. MinerU 编程接口
### 3.1 核心函数:`do_parse`
```python
from mineru.cli.common import do_parse, read_fn
from mineru.utils.enum_class import MakeMode
from pathlib import Path
def do_parse(
output_dir: str, # 输出目录路径
pdf_file_names: list[str], # PDF 文件名列表(不含扩展名)
pdf_bytes_list: list[bytes], # PDF 文件字节列表
p_lang_list: list[str], # 语言列表("ch", "en", "japan" 等)
backend: str = "pipeline", # "pipeline" | "vlm-auto-engine" | "hybrid-auto-engine"
parse_method: str = "auto", # "auto" | "txt" | "ocr"
formula_enable: bool = True,
table_enable: bool = True,
server_url: str | None = None, # 远程服务器 URL仅 http-client 后端)
f_dump_md: bool = True, # 输出 .md 文件
f_dump_middle_json: bool = True, # 输出 _middle.json
f_dump_model_output: bool = True, # 输出 _model.json
f_dump_orig_pdf: bool = True, # 输出原始 PDF 副本
f_dump_content_list: bool = True, # 输出 _content_list.json
f_draw_layout_bbox: bool = True, # 输出带布局框的 PDF
f_draw_span_bbox: bool = True, # 输出带 span 框的 PDF
f_make_md_mode: MakeMode = MakeMode.MM_MD, # Markdown 模式
start_page_id: int = 0,
end_page_id: int | None = None, # None = 所有页
**kwargs,
)
```
### 3.2 `read_fn` 辅助函数
```python
from mineru.cli.common import read_fn
# 读取 PDF 文件为 bytes
pdf_bytes = read_fn("F:/path/to/doc.pdf")
# 也支持图片文件(自动转为 PDF bytes
png_bytes = read_fn("scan.png")
```
### 3.3 输出目录结构
Pipeline 后端(`backend="pipeline"`)输出:
```
{output_dir}/
{pdf_name}/
auto/ # parse_method="auto"
{pdf_name}.md # ★ Markdown 输出(主要产物)
{pdf_name}_middle.json # 中间解析结果
{pdf_name}_model.json # 模型原始输出
{pdf_name}_content_list.json
{pdf_name}_origin.pdf # 原始 PDF 副本
{pdf_name}_layout.pdf # 布局可视化
{pdf_name}_span.pdf # Span 可视化
images/ # 提取的图片
```
### 3.4 语言代码
| 代码 | 语言 |
|------|------|
| `ch` | 简体中文 |
| `ch_server` | 中文服务器版(较快) |
| `ch_lite` | 中文轻量版 |
| `en` | 英语 |
| `japan` | 日语 |
| `korean` | 韩语 |
| `chinese_cht` | 繁体中文 |
---
## 4. 接入方案
### 方案 A直接异步 API 调用(强烈推荐,需 v3.2.2
升级到 v3.2.2 后,可以直接使用 `aio_do_parse()` — MinerU 原生异步接口,无需 `asyncio.to_thread()`
**优点**
- **原生 async**,直接 await不阻塞 FastAPI 事件循环
- 最简单,不需要进程间通信
- 可直接获取输出文件路径
**前提**
- FairScan PC 服务器在 MinerU conda 环境中运行
- `F:/datasets_rm/MinerU` 已通过 `pip install -e .` 安装
**实现思路**
```python
# ---- pc-server/main.py 新增代码 ----
from pathlib import Path
from mineru.cli.common import aio_do_parse, read_fn
async def real_mineru_processing(task_id: str):
"""使用 MinerU 异步接口真实处理 PDF"""
task = tasks_db.get(task_id)
if task is None:
return
file_name = task.get("fileName", "document.pdf")
base_name = Path(file_name).stem
upload_path = Path(task["uploadPath"])
process_type = task.get("processType", "ocrpdf")
lang = task.get("options", {}).get("lang", "ch")
task["status"] = "processing"
task["progress"] = 10
task["message"] = "MinerU processing started..."
output_dir = TASKS_DIR / task_id
output_dir.mkdir(exist_ok=True)
pdf_bytes = read_fn(upload_path)
try:
if process_type == "markdown":
await aio_do_parse(
output_dir=str(output_dir),
pdf_file_names=[base_name],
pdf_bytes_list=[pdf_bytes],
p_lang_list=[lang],
backend="pipeline",
f_dump_md=True,
f_dump_middle_json=False,
f_dump_model_output=False,
f_dump_orig_pdf=False,
f_dump_content_list=False,
f_draw_layout_bbox=False,
f_draw_span_bbox=False,
)
md_path = output_dir / base_name / "auto" / f"{base_name}.md"
if md_path.exists():
art_id = str(uuid.uuid4())
artifacts_db[task_id] = [{
"artifactId": art_id, "fileName": f"{base_name}.md",
"fileSize": md_path.stat().st_size, "fileType": "md",
"filePath": str(md_path),
}]
artifacts_map[art_id] = artifacts_db[task_id][0]
task.update(status="completed", progress=100,
message="MinerU Markdown completed")
return
elif process_type == "ocrpdf":
await aio_do_parse(
output_dir=str(output_dir),
pdf_file_names=[base_name],
pdf_bytes_list=[pdf_bytes],
p_lang_list=[lang],
backend="pipeline",
f_dump_md=False,
f_dump_middle_json=False,
f_dump_model_output=False,
f_dump_orig_pdf=False,
f_dump_content_list=False,
f_draw_layout_bbox=True,
f_draw_span_bbox=False,
)
layout_pdf = output_dir / base_name / "auto" / f"{base_name}_layout.pdf"
if layout_pdf.exists():
art_id = str(uuid.uuid4())
artifacts_db[task_id] = [{
"artifactId": art_id, "fileName": f"{base_name}_ocr.pdf",
"fileSize": layout_pdf.stat().st_size, "fileType": "pdf",
"filePath": str(layout_pdf),
}]
artifacts_map[art_id] = artifacts_db[task_id][0]
task.update(status="completed", progress=100,
message="OCR PDF completed")
return
task["status"] = "failed"
task["message"] = "MinerU did not produce output"
except Exception as e:
task["status"] = "failed"
task["message"] = f"MinerU error: {str(e)}"
logger.error(f"MinerU task {task_id} failed: {e}")
```
### 方案 B子进程调用备选
通过 `subprocess` 调用 `mineru` CLI
```python
import subprocess
import asyncio
async def mineru_subprocess(task_id: str):
task = tasks_db[task_id]
upload_path = task["uploadPath"]
output_dir = TASKS_DIR / task_id
cmd = [
r"D:/ProgramData/miniconda3/envs/MinerU/python.exe",
"-m", "mineru.cli.client",
"-p", str(upload_path),
"-o", str(output_dir),
"-b", "pipeline",
"-l", "ch",
]
proc = await asyncio.create_subprocess_exec(
*cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
# 轮询进度(可选:监控 stdout 中的进度信息)
while True:
line = await proc.stdout.readline()
if not line:
break
# 解析进度...
returncode = await proc.wait()
if returncode == 0:
task["status"] = "completed"
else:
task["status"] = "failed"
```
**优点**进程隔离MinerU 崩溃不影响 FairScan 服务。
**缺点**:进度监控困难,需要 IPC。
### 方案 CMinerU FastAPI 服务
运行 MinerU 自带的 FastAPI 服务 `mineru-api` 作为微服务FairScan 通过 HTTP 调用。
这一方案与 pc-api-spec.md 中对原子服务的建议一致,但实现复杂度更高。
---
## 5. 与 pc-api-spec.md 的对应关系
根据接口规范,两种 `processType` 与 MinerU 的映射:
| processType | MinerU 后端 | 输出文件 | 文件类型 |
|-------------|-----------|---------|---------|
| `markdown` | `backend="pipeline"` | `{name}.md` | `text/markdown` |
| `ocrpdf` | `backend="pipeline"` + `f_draw_layout_bbox=True` | `{name}_layout.pdf` | `application/pdf` |
两种类型共用同一个 MinerU `do_parse` 调用,仅输出选项不同。
---
## 6. 接入步骤建议
### Step 1升级 MinerU 到最新版
```bash
cd F:/datasets_rm/MinerU
git checkout main && git pull origin main
git checkout mineru-3.2.2-released
conda activate MinerU
pip install -e .
```
验证:
```bash
python -c "from mineru.cli.common import aio_do_parse; print('OK')"
```
### Step 2切换 PC 服务器运行环境
```bash
conda activate MinerU
cd E:/race_save/FairScan_cyy/FairScan/pc-server
python main.py
```
### Step 3替换 `simulate_processing` 为真实 MinerU 调用
`main.py` 中将 `simulate_processing` 替换为 `real_mineru_processing`(参考方案 A 的实现)。
### Step 4端到端测试
1. 用小 PDF1-2 页)先用 `parse_method="txt"` 测试(速度快)
2. 确认无误后切换为 `parse_method="auto"`(完整 OCR+公式+表格)
3. 测试处理完成后产物下载
---
## 7. 注意事项
| 项目 | 说明 |
|------|------|
| **GPU 显存** | RTX 4060 有 8GB VRAM。pipeline 后端约需 4-6GBVLM 后端约需 6-8GB。建议用 pipeline 后端。 |
| **处理速度** | 普通 A4 PDFpipeline 后端约 3-8 秒/页(取决于内容复杂度)。 |
| **语言** | 默认传 `ch`简体中文。FairScan 可扩展语言选择功能。 |
| **页数限制** | 可用 `start_page_id` / `end_page_id` 限制处理范围。 |
| **大文件** | PDF > 100 页建议分批处理。 |
| **超时** | 单次处理时间与页数成正比,不要设置过短的 HTTP 超时。 |
| **锁模型** | `do_parse` 不是线程安全的。FastAPI 的 `async` 端点应在线程池中调用,避免阻塞事件循环。 |
| **错误处理** | `do_parse` 出错会抛出异常,需捕获并设置 `task["status"] = "failed"`。 |
---
## 8. 关键参考文件
| 文件 | 说明 |
|------|------|
| `F:/datasets_rm/MinerU/mineru/cli/common.py` | `do_parse()` 主入口 |
| `F:/datasets_rm/MinerU/mineru/cli/client.py` | CLI 参数定义 |
| `F:/datasets_rm/MinerU/mineru/cli/output_paths.py` | 输出路径解析 |
| `F:/datasets_rm/MinerU/mineru/utils/config_reader.py` | 配置读取 |
| `F:/datasets_rm/MinerU/mineru/utils/enum_class.py` | 枚举类型定义 |
| `F:/datasets_rm/MinerU/mineru.template.json` | 配置文件模板 |
| `E:/race_save/FairScan_cyy/FairScan/pc-server/main.py` | FairScan PC 服务器(需修改) |
| `E:/race_save/FairScan_cyy/FairScan/requirements/pc-api-spec.md` | API 接口规范 |