2026-05-03-23-22-10 修复阅览切换和下载触发

This commit is contained in:
2026-05-03 23:26:36 +08:00
parent bff7eead08
commit 7b7c555321
6 changed files with 304 additions and 10 deletions

View File

@@ -0,0 +1,55 @@
# 实现方案
开始时间2026-05-03-23-22-10
## 本次方案路径
`工程分析/实现方案-2026-05-03-23-22-10.md`
## 实现目标
修复 DICOM 阅览切片/显示模式切换导致后端连接重置的问题,并降低 ZIP 下载误触发和 HTTP 文件加载警告。
## 涉及文件
- `web_backend.py`
- `WebSite/src/App.tsx`
- `工程分析/经验记录.md`
## 执行步骤
1. 后端增加 DICOM 体数据缓存:
- 增加 `DICOM_VOLUME_CACHE` 和锁。
- 基于 DICOM 目录路径、文件数量、最新修改时间生成签名。
- `/api/library/reformat-preview` 复用缓存体数据,避免每次切片/窗位变化都重新读取完整 DICOM。
- 控制缓存规模,避免多个大体数据长期驻留。
2. 后端下载容错:
- `send_file` 捕获 `BrokenPipeError``ConnectionResetError``ConnectionAbortedError`,避免客户端中断下载时污染日志或影响服务。
3. 前端 DICOM 阅览请求防抖:
- 增加 `debouncedViewerSliceIndex`
- 切片滑杆变化后延迟短时间再请求后端,减少快速拖动时的请求数量。
- 显示模式或平面切换仍可即时重置到中间切片,但实际请求走防抖后的切片索引。
4. 前端 ZIP 下载防重复:
- 增加已下载 ZIP job id 记录,确保同一个 ZIP job 自动下载只触发一次。
5. 前端 ZIP 下载方式调整:
- 对后端文件路径优先使用 `fetch` 获取 blob再使用本地 `blob:` URL 触发下载。
- 如 blob 下载失败,回退到原有 `/api/file` URL 方式,并提示用户。
6. 执行测试方案。
7. 更新 `工程分析/经验记录.md`
8. 提交并推送 Giteacommit 信息使用 `2026-05-03-23-22-10 修复阅览切换和下载触发`
9. 重新部署到 `http://192.168.3.11:3005/`
## 回滚思路
若缓存或 blob 下载出现问题,可回滚本次 `web_backend.py``WebSite/src/App.tsx` 修改,恢复直接读取体数据和直接 URL 下载方式。
## 风险控制
- 体数据缓存只影响阅览预览接口,不影响真实形变输出。
- 缓存使用目录签名,影像库数据变化后会重新读取。
- 删除影像库时同步清除 DICOM 文件缓存和体数据缓存。
- 前端保留下载回退路径,避免 blob 下载失败后无法下载。
## 人工审核状态
用户已明确本次不需要人工二次确认,直接执行。

View File

@@ -0,0 +1,78 @@
# 测试方案
开始时间2026-05-03-23-22-10
## 本次方案路径
`工程分析/测试方案-2026-05-03-23-22-10.md`
## 测试范围
- DICOM 阅览冠状位/矢状位请求是否稳定。
- 切片滑杆快速变化是否不再导致后端被杀或连接重置。
- 显示模式切换是否正常返回图片。
- ZIP 下载 job 完成后是否只触发一次下载。
- Python 语法、前端类型检查、构建是否通过。
- 重新部署后服务是否可访问。
## 测试命令
Python 语法检查:
```bash
python -m py_compile web_backend.py head_extension_app.py
```
前端类型检查:
```bash
cd WebSite
npm run lint
```
前端构建:
```bash
cd WebSite
npm run build
```
后端阅览接口压力抽测:
```bash
for i in 0 64 128 256 384 511; do
curl -s "http://127.0.0.1:8787/api/library/reformat-preview?id=demo_ori_head_ct&plane=coronal&index=$i&window=bone" >/dev/null
done
```
部署验证:
```bash
curl -I --max-time 5 http://192.168.3.11:3005/
curl -s --max-time 10 "http://127.0.0.1:8787/api/library/reformat-preview?id=demo_ori_head_ct&plane=coronal&index=511&window=bone"
```
## 手工验证点
- 打开数据影像库,进入 DICOM 阅览。
- 拖动切片滑杆,图像更新且控制台不再出现 `ERR_CONNECTION_RESET`
- 切换显示模式,图像更新且后端服务保持在线。
- 如有 ZIP 打包下载任务,完成后只自动下载一次。
## 验收标准
- 后端不再因为阅览切片/窗位切换被 `Killed`
- `reformat-preview` 连续请求正常返回 JSON。
- `npm run lint` 通过。
- `npm run build` 通过。
- `python -m py_compile web_backend.py head_extension_app.py` 通过。
- Gitea commit/push 完成。
- 重新部署后 `http://192.168.3.11:3005/` 返回 `200 OK`
## 残余风险
- 纯 HTTP 部署下,浏览器可能仍会对下载文件给出安全提示;本次通过 blob 下载和重复触发保护尽量降低该提示出现概率。彻底消除需要 HTTPS 部署。
## 人工审核状态
用户已明确本次不需要人工二次确认,直接执行。

View File

@@ -55,3 +55,21 @@ C. 解决问题方案
D. 后续如何避免问题
已有后端能力应先通过前端入口复用不重复实现打包逻辑。DICOM 阅览类预览需要使用缓存,避免每次切换切片或窗宽窗位都重复生成相同图像;新增缓存应继续放在现有 `_preview_cache` 下,避免污染仓库。
## 2026-05-03-23-22-10 修复阅览切换和下载触发
A. 具体问题
DICOM 阅览切片滑杆和显示模式切换时,浏览器出现 `ERR_CONNECTION_RESET`;同时 ZIP 文件下载可能在其他 UI 操作期间再次触发,并出现 HTTP 文件加载安全提示。
B. 产生问题原因
阅览重建接口每次请求都重新读取整套 DICOM 体数据,切片滑杆快速拖动和显示模式切换会并发触发多个重建请求。前端 AbortController 只能取消浏览器等待,不能停止后端已经开始的 DICOM 读取容易造成内存压力并导致后端被系统终止。ZIP 下载通过直接访问后端 `/api/file` URL 触发,且缺少已完成 ZIP job 的一次性下载保护。
C. 解决问题方案
后端为 DICOM 体数据增加基于目录签名的内存缓存,并限制缓存数量;阅览接口复用缓存体数据生成不同平面、切片和窗宽窗位的 PNG。前端为阅览切片请求增加防抖减少滑杆拖动时的请求数量。ZIP 下载增加已下载 job id 记录,并优先使用 fetch blob 转本地 `blob:` URL 触发下载,失败时再回退到直接 URL。
D. 后续如何避免问题
涉及整套 DICOM 体数据读取的接口必须考虑缓存、并发和请求节流,不能把滑杆这类高频 UI 操作直接绑定到重型后端计算。自动下载类逻辑必须记录已处理 job避免同一个完成状态在后续渲染或轮询中重复触发。

View File

@@ -0,0 +1,55 @@
# 需求分析
开始时间2026-05-03-23-22-10
## 原始需求
用户要求严格使用代码编纂工作流,并在最开始确认整体流程。本次需求分析、实现方案、测试方案和执行修改均不需要用户二次人工确认。
需要修复两个问题:
1. 点击 DICOM 阅览中的切片滚动条时出现与 ZIP 文件相关的浏览器警告:`The file at ...head_ct_morph_selected_54ed40feeb63.zip was loaded over an insecure connection. This file should be served over HTTPS.`
2. 切换 DICOM 阅览显示模式时出现请求失败:`GET /api/library/reformat-preview ... net::ERR_CONNECTION_RESET`
## 目标
- DICOM 阅览切片滚动和显示模式切换不再导致后端连接重置。
- 降低切片滑杆快速拖动时的请求风暴和后端内存压力。
- ZIP 下载完成后只触发一次下载,避免和其他 UI 操作交织造成误触发。
- 尽量避免通过直接加载 ZIP URL 的方式触发浏览器 insecure file 警告。
## 影响范围
- `web_backend.py`
- DICOM 阅览重建预览接口。
- DICOM 体数据读取和缓存策略。
- 文件下载响应的连接中断容错。
- `WebSite/src/App.tsx`
- DICOM 阅览请求节流/防抖。
- ZIP 下载触发逻辑。
- 已完成 ZIP job 的重复下载保护。
- `工程分析/经验记录.md`
## 当前定位
- 后端日志显示 `python ../web_backend.py` 被系统 `Killed`,符合快速并发读取整套 DICOM 体数据导致内存压力过大的表现。
- 当前 `/api/library/reformat-preview` 每次请求都会调用 `load_dicom_volume(item["dicomPath"])`,快速拖动滑杆或切换窗位会并发生成多个请求。
- 前端阅览 effect 在 `viewerSliceIndex``viewerWindow` 等状态变化时立即发请求AbortController 只中断浏览器端等待,不能中止后端已经开始的体数据读取。
- ZIP 下载目前通过临时 `<a>` 直接访问后端 `/api/file?path=...zip`,浏览器可能显示 HTTP 文件下载安全警告;同时需要防止同一个 ZIP job 多次自动触发下载。
## 约束
- 不需要用户二次确认,可以直接执行修改。
- 不改变真实 DICOM 形变算法。
- 不引入大型前端 DICOM 阅片库。
- 不把运行缓存图片、ZIP、DICOM 或构建产物提交到仓库。
## 风险点
- 体数据内存缓存会占用一定内存,需要按影像库目录签名复用并限制缓存规模。
- Blob 下载大 ZIP 会占用浏览器内存;本次优先用于减少直接 URL 加载警告,仍需关注超大 ZIP 的浏览器表现。
- 浏览器对 HTTP 下载的安全提示无法在纯 HTTP 部署下彻底消除;如果必须完全消除,需要 HTTPS 部署。
## 待确认事项
用户已明确本次不需要二次人工确认,因此本次文档写完后直接执行实现和测试。