zip deformation outputs on download

This commit is contained in:
2026-05-03 01:33:18 +08:00
parent 0aa9cffb97
commit a795aa13bf
2 changed files with 78 additions and 84 deletions

View File

@@ -430,40 +430,6 @@ def zip_folder(source_dir, zip_path):
return zip_path
def zip_result_bundle(zip_path, state_zips, preview_paths):
zip_path = Path(zip_path).resolve()
safe_mkdir(zip_path.parent)
if zip_path.exists():
zip_path.unlink()
comparison_path = Path(preview_paths["comparison"]).resolve()
screenshots_dir = Path(preview_paths["screenshots"]).resolve()
with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as archive:
for state_key, state_zip in state_zips.items():
source_zip = Path(state_zip).resolve()
archive.write(
source_zip,
f"dicom_zips/{source_zip.name}",
compress_type=zipfile.ZIP_STORED,
)
if comparison_path.exists():
archive.write(
comparison_path,
f"previews/{comparison_path.name}",
)
if screenshots_dir.exists():
for file_path in screenshots_dir.rglob("*"):
if file_path.is_file():
archive.write(
file_path,
Path("previews/process_screenshots") / file_path.relative_to(screenshots_dir),
)
return zip_path
def read_user_tasks():
tasks = read_json_file(USER_TASKS_META, {})
return tasks if isinstance(tasks, dict) else {}
@@ -595,30 +561,51 @@ def make_preview(input_dir, angle_degrees):
}
def zip_metadata(zip_path):
zip_path = Path(zip_path).resolve()
return {
"path": str(zip_path),
"name": zip_path.name,
"size": zip_path.stat().st_size,
}
def serialize_outputs(output_paths, preview_paths, zip_path=None, state_zips=None):
def serialize_outputs(output_paths, preview_paths):
return {
"outputs": {key: str(Path(value).resolve()) for key, value in output_paths.items()},
"previews": {
"comparison": str(Path(preview_paths["comparison"]).resolve()),
"screenshots": str(Path(preview_paths["screenshots"]).resolve()),
},
"zip": zip_metadata(zip_path) if zip_path else None,
"stateZips": {
key: zip_metadata(value)
for key, value in (state_zips or {}).items()
},
"zip": None,
"stateZips": {},
}
def prepare_deformation_zip(job_id, target):
job = get_job(job_id)
if not job:
raise RuntimeError("任务不存在。")
if job.get("kind") != "deformation":
raise RuntimeError("该任务不是四状态生成任务。")
if job.get("status") != "completed":
raise RuntimeError("四状态任务尚未完成,无法下载。")
result = job.get("result") or {}
outputs = result.get("outputs") or {}
job_root = RESULT_DIR / job_id
if target == "all":
output_dir = job_root / "four_state_output"
if not output_dir.exists():
raise RuntimeError("四状态输出目录不存在。")
return zip_folder(output_dir, job_root / f"head_ct_morph_{job_id}.zip")
state_labels = {
"original": "original",
"hard_boundary": "hard_boundary",
"gaussian_smooth": "gaussian_smooth",
"soft_transition": "soft_transition",
}
if target not in state_labels:
raise RuntimeError("未知的四状态下载类型。")
source_dir = Path(outputs.get(target, ""))
if not source_dir.exists():
raise RuntimeError("该状态的 DICOM 输出目录不存在。")
return zip_folder(source_dir, job_root / f"{target}_{job_id}.zip")
class Handler(BaseHTTPRequestHandler):
def log_message(self, format, *args):
print("%s - %s" % (self.address_string(), format % args))
@@ -679,6 +666,17 @@ class Handler(BaseHTTPRequestHandler):
self.send_json({"job": get_user_task_job(username, kind)})
return
if parsed.path == "/api/deformation/download":
params = parse_qs(parsed.query)
job_id = params.get("job", [""])[0]
target = params.get("target", ["all"])[0]
try:
zip_path = prepare_deformation_zip(job_id, target)
self.send_file(zip_path, "application/zip", as_attachment=True)
except Exception as exc:
self.send_json({"error": str(exc)}, status=400)
return
if parsed.path == "/api/file":
params = parse_qs(parsed.query)
file_path = Path(unquote(params.get("path", [""])[0])).resolve()
@@ -692,15 +690,11 @@ class Handler(BaseHTTPRequestHandler):
content_type = "video/mp4"
elif file_path.suffix.lower() == ".zip":
content_type = "application/zip"
data = file_path.read_bytes()
self.send_response(200)
self.send_cors_headers()
self.send_header("Content-Type", content_type)
if file_path.suffix.lower() == ".zip":
self.send_header("Content-Disposition", f'attachment; filename="{file_path.name}"')
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
self.send_file(
file_path,
content_type,
as_attachment=file_path.suffix.lower() == ".zip",
)
return
self.send_json({"error": "接口不存在。"}, status=404)
@@ -743,25 +737,7 @@ class Handler(BaseHTTPRequestHandler):
transition_width,
progress,
)
set_job(job_id, message="正在打包各状态 DICOM ZIP...", progress=88)
state_zips = {}
for state_key in [
"original",
"hard_boundary",
"gaussian_smooth",
"soft_transition",
]:
state_zips[state_key] = zip_folder(
output_paths[state_key],
job_root / f"{state_key}_{job_id}.zip",
)
set_job(job_id, message="正在整理四状态总 ZIP...", progress=96)
zip_path = zip_result_bundle(
job_root / f"head_ct_morph_{job_id}.zip",
state_zips,
preview_paths,
)
return serialize_outputs(output_paths, preview_paths, zip_path, state_zips)
return serialize_outputs(output_paths, preview_paths)
self.send_json(
start_job(
@@ -848,6 +824,22 @@ class Handler(BaseHTTPRequestHandler):
return {}
return json.loads(body.decode("utf-8"))
def send_file(self, file_path, content_type="application/octet-stream", as_attachment=False):
file_path = Path(file_path).resolve()
if not file_path.exists() or not file_path.is_file():
self.send_json({"error": "文件不存在。"}, status=404)
return
self.send_response(200)
self.send_cors_headers()
self.send_header("Content-Type", content_type)
if as_attachment:
self.send_header("Content-Disposition", f'attachment; filename="{file_path.name}"')
self.send_header("Content-Length", str(file_path.stat().st_size))
self.end_headers()
with file_path.open("rb") as file_handle:
shutil.copyfileobj(file_handle, self.wfile)
def send_cors_headers(self):
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Access-Control-Allow-Methods", "GET,POST,OPTIONS")