跨获批任务的原生续传
这是可选扩展,原始 LocalSend v2 握手、必需字段、证书校验和最终文件原字节不变。它与管理员 Bearer API、网页下载管理相互独立;原版客户端无需修改。
使用流程与适用目标
发送队列先持久化每个文件的随机恢复键。整任务重试、单个未完成文件重试继承原文件的键;重新选择创建的新任务使用新键。恢复队列不会自动发送。每次重试先重新走原版批准、取得新文件令牌,再查找已确认块。
双方须支持可选能力;接收目标须为持久普通目录,支持可核验的目录/文件身份和独占锁,发送源须为至少1 MiB的可定位普通文件。相册、临时缓存和Android SAF目标不广告持久续传,继续保留既有兼容传输。批准前检查目标能力,文件长度及整文件SHA-256仍须吻合;恢复键本身不是身份或授权。
线上契约
沿用同会话续传的地址及鉴权查询:/api/legnasend/v1/receive-resume/。核对来源IP、当前会话/文件令牌及TLS证书身份,管理员密钥不替代这些条件。
原能力响应可选增加:
{"version":1,"supported":true,"blockSize":1048576,"durable":{"version":1}}只有看到durable.version == 1且已保存本文件恢复键,才在POST open增加:
{"size":2097153,"sha256":"ACTUAL_64_HEX_SHA256","recovery":{"version":1,"resumeKey":"11111111-1111-4111-8111-111111111111"}}没有协商持久能力时,原两字段正文保持不变,兼容会拒绝未知字段的旧LegnaSend。原版对端在能力不支持时走原v2上传;鉴权错误、契约损坏或源变化不静默降级后重传。
回执保留原字段并增加verifiedBytes。持久open可以快速返回state: "verifying", offset: 0,后台核对旧缓存或成品;用新的resumeId轮询GET status。校验期间block/finish返回409。核验字节与传输进度独立,不是可发送偏移或网络速度。核验完成后ready/receiving只返回连续已确认前缀,不能用离散块总数冒充;complete必须核验成品整文件摘要。
POST suspend使用共同查询加当前resumeId,无正文;持久化检查点并释放实际事务后返回suspended。此操作仅用于持久能力。原abort清理仍附着的自有半下载,不删除成品;已分离记录不得由过时会话/令牌重新认领删除。继续恢复须新批准,本地维护可清理非活动自有记录。
每次确认仍为原来的1 MiB块或最后短块,保留块SHA-256头。不同文件不聚合,单文件块仍顺序发送,.ls不成为最终文件格式。
校验、中断与生命周期
网络重连保持有界退避;持久校验另有可取消轮询,发送端期限30分钟,不消耗原三次普通重连次数。源文件摘要和接收数据校验显示独立阶段,恢复偏移重置速度采样基线,不产生虚假吞吐峰值。
重连耗尽时尝试文件级suspend,区分确认保留与结果未知。suspend应答未知时不再追加通用v2取消,避免不同连接乱序删除正在分离的检查点。明确取消活动传输及源变化仍abort;此前已发布成功的文件不因应答丢失改成失败。
私有登记最多128条,新记录绝对保留期限为 1 小时;状态轮询和新批准不延长期限。重新采用前核对来源、获批目录/名称、缓存身份和所有已确认块。目标缺少出生时间或锁能力时不启用此扩展,而不是降低身份核验。源变化只清理经过身份核验的旧自有缓存,其他文件及已被替换路径保持不动。
崩溃恢复不恢复旧网络令牌或旧会话。新尝试在跨进程独占锁下打开登记;发布确认前持久化发布意图及回执UUID。丢最终应答后的重试核验自有成品并复用回执,避免新增编号副本或重复历史。清空/删除历史的抑制记录也跨重启保存。
默认孤立缓存清理与有效续传租约分开。启动/API维护保留未到期的非活动恢复记录;本地页内明确确认的清理可提前删除。活动锁、成品、未知.ls和身份不符的文件继续保护。到期由后续操作/维护处理,不保证某个时刻自动立即删除。
验证与剩余边界
tests/durable_receive_resume.rs通过真实HTTP覆盖新批准、新令牌/resumeId、旧会话隔离、确认偏移、服务器替换、真实发送客户端续传、源变化清理、明确abort和最终应答丢失。父测试显式启动独立子进程,块确认后强杀,再由新服务器恢复并核对最终原字节;子测试标记ignored只是防止被测试框架直接单独运行,不表示父测试跳过强杀。
登记测试覆盖身份/锁、缺块、错误成品内容和保守清理;独立模拟线上对端验证发送协商与旧协议路径。这些是本机自动化,不替代五平台真机验收。提供程序持久接收、完整移动后台和原生单文件多块并发仍待完成。
类型化恢复状态
私有应用事件区分等待重试、可重试失败、需要授权、来源变化及响应无效。保留状态独立表达已确认检查点、结果未知、或本次尝试无可复用检查点;最后一种不代表接收端磁盘已全部清理。可选HTTP状态只接受数字,不解析任意响应正文来推断永久来源结束;原版对端404/410本身也不足以证明永久失效。
等待表示实际有界重试,包括检查点忙碌,不等同网卡已断联。完整性校验轮询具有独立预算,不计入传输字节。来源变化阻止直接复用旧文件恢复键,须选择新来源/新建发送。文件与任务面板显示相同类型状态,取消只清理自身的活动等待提示。
固定 Rust 标准库在 Android 返回不支持,因此该平台使用真实系统非阻塞锁。仅文件系统明确以 ENOSYS 或 EOPNOTSUPP 拒绝 flock 时才可采用整文件 OFD 锁;锁竞争不切换锁域,显式解锁使用实际获得的锁类型。描述符控制锁生命周期,其它平台错误仍失败。下文的独立来源结束授权链才负责受权远端清理,不因状态显示或本地取消就声称已完成。
可选来源结束通知
用户明确结束来源时,即使发送端离线,也可以先持久保存意图,在同一接收端重新可达后通知。这是单独协商的授权,不复用恢复键、旧会话或文件令牌,也不是管理 API 密钥的权限。原版 LocalSend 和未支持此能力的持久恢复对端保持原有行为;本地取消不证明远端缓存已经删除。
接收端公布 durable.sourceEnd: {"version":1}。只有支持该能力且在请求中设置 recovery.sourceEnd:1 的发送端,才会在就绪或状态回执中收到 sourceEnd 授权,字段为 version、grantId、round、随机 32 字节经 base64url 编码的 token 及 expiresAtUnixMs。接收端只持久保存密钥哈希。授权绑定缓存事务和原证书;关闭 TLS 时严格绑定原 IP。每次重新批准后的附加都会轮换授权,不延长原记录的绝对期限。
发送第一个文件块之前,应用先把授权保存到独立私有日志,再确认精确的原生上传请求。否认、等待超时或取消均不发送首块。日志使用不透明原生租约,禁止跟随链接,核验目录及锁身份,持有真实进程锁,Unix 权限仅限当前用户。写入结果不确定时禁止继续写,重新打开并读取真实磁盘状态后才能恢复。Windows 使用应用支持目录继承的私有权限,不声明 Windows 断电持久性已经验收。
应用先保存来源结束意图,再派发通知。相同恢复来源仍被其它活跃任务引用时延迟通知,明确结束的恢复键不用于新的发送。通过重新发现核实已知对端,不盲用旧地址。临时网络故障保持待通知;不支持、过期、已替代、鉴权失败、结果未知都不是删除回执。已经发布的文件保留。公共 API 重试仅安排已有独立授权的通知,只显示脱敏状态,不暴露密钥和本地路径。
独立请求格式见来源结束线协议。接收端最多使用四个实际清理工作线程,私有授权、轮次和证明总数最多 256 条。活跃写入及正在发布的事务不强制删除。清理成功必须来自身份核验后的真实删除和持久回执,重复相同授权返回原回执。正常显式丢弃、维护及到期清理在授权仍有效时保留清理证明;缺少有效授权或证明只返回未知,不虚构成功。
iOS 协调目录恢复(2026-09-27)
外部“文件”接收目录现会在重新取得安全作用域与协调访问后执行相同持久恢复探测。权限、身份或锁探测失败则不协商持久恢复。仍须重新批准原始协议会话,记住的路径和旧令牌都不授予恢复写入权。
来源结束先核验私有登记,才向宿主申请其中保存的批准根;授权后再次核验来源凭据、目录和文件身份。宿主持有访问权直到实际阻塞工作结束,包括 HTTP 断开和监听停止。过期/手动清理及只读盘点同样在获批根内执行,返回可去重的不透明条目结果。
尚未取得保存目标就取消的外部目录尝试,只归还私有登记所有权,不在接收作用域释放后异步清理目标。已登记部分内容保留给重新批准的重试,或后续协调来源结束/过期清理。
旧版签发的一天记录继续按原到期时间处理,不在升级时提前失效。
持有排他锁的活动写入不会因续传授权到期而中止;活动仅延后本地残留清理,不延长已公布的续传期限或来源结束授权。过期后重新连接的采用请求仍被拒绝。未知或未来活动时间保守保留数据。