「导出之后可以放心删掉」——唯有支持导入恢复,这项承诺才算成立
档案导出到硬盘之后,扩展内部存储即可按需清理。但若未实现导入机制,清空数据将导致后续增量采集退化为耗时的全量重抓。
界面上一直提示:导出之后可以安全删除扩展内的本地副本。 这项承诺唯有在支持完整导入恢复时才真正成立。 否则下一次增量采集将失去比对基准,被迫退化为长达数小时、 重复请求数千个详情页的全量抓取。
因此本周完成了档案导入功能:用户指定此前导出的归档目录,即可完整还原至扩展内部。
它必须成为第二条合法的归档写入路径
项目架构原本有一项严格约定:归档存储仅允许单一路径执行写入。 导入功能打破了这一假设——浏览器窗口环境是唯一能同时获取目录句柄与存储上下文的节点, 且大量二进制数据无法直接经由内部 JSON 消息通道传输。
为此需要重新审视该约束的本质目的:防止已有归档数据的物理偏移量遭到破坏。 索引文件中严格记录了每个分段的起始偏移与长度,任何原位覆写都会导致整份档案索引失效。
因此导入专用的 Worker 进程被严格限制为仅允许创建新文件,禁止修改任何已存在的数据; 且判断标准直接以存储层「目标文件此刻是否真实存在」为准,而非轻信调用方的单方面声明。 这一设计相比单纯限制「必须为空目录」更加精准且实用——为中断的导出继续补齐缺失文件 天然具备合法性(缺失文件属于新建操作),而后者会一刀切地阻断此类恢复, 迫使用户手动删除残留文件。
认 bundle_id,不认目录名
用户会改名,会把它放进「备份/2026-08/」,解压两遍会得到「xxx (1)」。
真相在 manifest.json 和索引文件名里,而这几处必须互相对得上。
这条检查第一版写错了,错得很值得记:实现只取了第一个索引文件, 而排序之后先出现的那个恰好与 manifest 对得上—— 于是「三处必须一致」全票通过,另一份档案的段文件跟着一起导了进来。 必须收全部索引文件再比。
每一种「不导」都有自己的名字
已经有了 / 补齐 / 重复 / 编号撞了 / 别的账号 / 不能导 / 正在抓—— 七种情况,七句话。合成一句「跳过」等于什么都没说。
严格拒绝导入的风险几乎为零,本地磁盘上的源文件依然完好;而一旦放行损坏的档案,将产出 表面看似正常但底层不可用的破损归档:能选中、能导出、能被解析器读, 只是索引里的偏移量指向不存在的字节。
顺带修掉两个线上的 bug,它们都是同一件事的后果
上周把面板从一个 3034 行的文件拆成了一层壳加十个模块。 拆开之后才看得见三处原来看不见的耦合——在一个文件里, 「模块级变量」看起来只是变量,分开了才知道谁在动谁的东西, 实测有两处真的构成了环。
而这次发现的两个 bug,正是那种「只有真的跑一遍才会露出来」的:
-
setStorageUsage(summarizeBundles)({...})——括号位置错了, 存储页一打开就抛。任何静态检查都看不出来(它是「调用一次调用的结果」, 没有名字缺失),而那几个面板模块此前一次都没被真的执行过。 -
「删除这一份」直接抛 ReferenceError:用了一个没 import 的名字。
本来就有一条测试专门防这个,却漏了——它只扫调用位置
(
foo(),而if (!currentBundleId)不是调用。 现在扫全部引用位置,为此把那个正则剥离器换成了真正的字符扫描器。
存储页也并进了档案页。两页列的是同一批档案,只换了几列; 两份列表意味着两处各自记得失效、各自扫一遍目录, 而用户还得在两页之间对「刚才看的是哪一份」。