Files

50 lines
1.7 KiB
Python

"""
分卷接收模块 (receiver)
模块: 服务端 / 分卷接收
输入: 卷数据 (transfer_id, index)
输出: 落盘卷 + ok/409
逐卷校验 SHA-256 (对照 init 登记的规格); 重复卷直接 ok (幂等, 断点续传重发不炸)。
"""
import hashlib
import shutil
from pathlib import Path
from task_manager import TaskManager
class ReceiverError(RuntimeError):
"""卷校验失败 (哈希不一致/规格缺失)"""
class Receiver:
def __init__(self, tasks: TaskManager, tmp_root: str | Path) -> None:
self.tasks = tasks
self.tmp_root = Path(tmp_root)
def receive(self, transfer_id: str, idx: int, data: bytes) -> None:
"""接收并校验一卷。失败抛 ReceiverError (api 转 409)。"""
if self.tasks.is_chunk_received(transfer_id, idx):
return # 幂等: 重复卷直接 ok
spec = self.tasks.chunk_spec(transfer_id, idx)
if spec is None:
raise ReceiverError(f"任务 {transfer_id}{idx} 规格不存在")
# 校验大小 + SHA-256
if len(data) != spec["size"]:
raise ReceiverError(f"卷 {idx} 大小不符: 期望 {spec['size']}, 实际 {len(data)}")
sha256 = hashlib.sha256(data).hexdigest()
if sha256 != spec["sha256"]:
raise ReceiverError(f"卷 {idx} SHA-256 不一致 (数据损坏或串卷)")
# 落盘临时目录
chunk_dir = self.tmp_root / transfer_id
chunk_dir.mkdir(parents=True, exist_ok=True)
chunk_path = chunk_dir / f"chunk_{idx:04d}"
tmp_path = chunk_path.with_suffix(".tmp")
tmp_path.write_bytes(data)
shutil.move(str(tmp_path), str(chunk_path))
self.tasks.mark_chunk_received(transfer_id, idx)