news 2026/7/19 11:42:01

45-崩溃后草稿损坏-用临时文件和恢复账本保护输入

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
45-崩溃后草稿损坏-用临时文件和恢复账本保护输入

第45篇|崩溃后草稿损坏:用临时文件和恢复账本保护输入

摘要:编辑页草稿最怕“写了一半崩溃,再打开内容坏了”。如果每次输入都直接覆盖正式草稿,崩溃、磁盘异常或序列化失败都会把可恢复内容一起毁掉。更稳的做法是写临时文件和恢复账本,完整写入后再原子替换,重启时按账本恢复或隔离损坏草稿。

有个长文编辑页,用户写到一半切后台,应用被系统回收。重进后草稿还在,但正文少了后半段,图片列表也乱了。原代码在每次输入后直接覆盖draft.json,中途写入失败就留下了半截 JSON。草稿保存不能只追求实时,要保证任何一次崩溃都不会破坏上一份完整内容。

这篇文章解决四个实际问题:

  1. 草稿写入先落临时文件,成功后再替换正式文件。
  2. 恢复账本记录最后一次完整版本和待恢复版本。
  3. 重启时先校验 JSON 和版本,不直接打开损坏草稿。
  4. 保存频率用防抖和显式保存结合,避免每个字符都落盘。


先确认损坏的是内容还是索引

草稿损坏不一定是文件消失。可能是 JSON 半截、图片附件缺失、版本不匹配,或者页面读取了旧索引。

位置现象风险处理方向
正文少半截直接覆盖正式文件崩溃留下半写入临时文件加替换
图片错位附件和正文不同步索引先更新了快照统一保存
重启空白解析失败被当成空草稿损坏隔离
频繁卡顿每字符写文件防抖保存

这一步的价值是先把问题放回运行链路。只要能确认问题停在哪个位置,后面就不用靠猜测改页面。

编辑状态和持久草稿要隔一层快照

页面里的输入状态变化很频繁,持久化层需要的是完整快照。不要让每个输入框都直接写草稿文件。

模块应该负责不应该负责
EditorPage当前输入、保存中提示文件路径
DraftSnapshotBuilder把页面状态转成快照磁盘写入
DraftStore临时文件和替换UI 布局
DraftJournal版本、路径、恢复状态输入事件
RecoveryPage恢复或丢弃选择序列化细节

边界定清后,代码就不会在页面、服务和回调之间来回复制同一段判断。后续新增场景也能先判断应该落在哪一层。

草稿快照要有版本和校验字段

exportinterfaceDraftSnapshot{draftId:stringversion:numbertitle:stringbody:stringattachmentIds:string[]savedAt:number}exportinterfaceDraftJournalEntry{draftId:stringstableVersion:numberpendingVersion:numberrecovering:booleanupdatedAt:number}exportfunctionisValidDraft(snapshot:DraftSnapshot):boolean{returnsnapshot.draftId.length>0&&snapshot.version>0&&snapshot.savedAt>0}

版本号和账本让重启恢复有依据。页面不能把解析失败的草稿直接当成空内容,否则用户会以为内容被正常清掉了。

这类模型最好放在modelscommon中,页面、Service 和仓储都使用同一份类型,避免各层用字符串互相猜。

写入走临时文件,再替换正式文件

exportclassDraftStore{constructor(privatereadonlyfileWriter:DraftFileWriter){}asyncsave(snapshot:DraftSnapshot):Promise<void>{consttempPath=draftTempPath(snapshot.draftId,snapshot.version)conststablePath=draftStablePath(snapshot.draftId)awaitthis.fileWriter.writeText(tempPath,JSON.stringify(snapshot))constloaded=JSON.parse(awaitthis.fileWriter.readText(tempPath))asDraftSnapshotif(!isValidDraft(loaded)){thrownewError('invalid draft snapshot')}awaitthis.fileWriter.replace(tempPath,stablePath)}}

先写 temp,再读回校验,最后替换正式文件。即使写 temp 过程中崩溃,上一份 stable 草稿仍然存在。

Service 的目标不是把所有逻辑都塞进去,而是把跨页面、跨生命周期或需要持久化的判断集中起来。页面只表达用户动作。

页面用防抖保存完整快照

@Componentstruct ArticleEditorPage{@Statetitle:string=''@Statebody:string=''@Statesaving:boolean=falseprivateautosave:DraftAutosaveService=createDraftAutosaveService()onInputChanged():void{constsnapshot=this.buildSnapshot()this.autosave.schedule(snapshot)}privatebuildSnapshot():DraftSnapshot{return{draftId:currentDraftId(),version:Date.now(),title:this.title,body:this.body,attachmentIds:readAttachmentIds(),savedAt:Date.now()}}}

页面只负责生成完整快照,不直接写文件。附件 id 和正文一起进入快照,避免正文恢复了、附件索引没恢复。

页面层要控制展示和交互节奏,但不应该拥有底层事实。这样页面重建、横竖屏变化或返回前台时,都能重新从 Service 拿到可信结果。

恢复时先隔离损坏版本

exportclassDraftRecoveryService{constructor(privatereadonlystore:DraftStore,privatereadonlyjournal:DraftJournal){}asyncrecover(draftId:string):Promise<DraftSnapshot|null>{conststable=awaitthis.store.loadStable(draftId).catch(()=>null)if(stable!==null&&isValidDraft(stable)){awaitthis.journal.markRecovered(draftId,stable.version)returnstable}awaitthis.journal.markCorrupted(draftId)returnnull}}

恢复服务不把坏文件吞掉。能恢复就标记已恢复,不能恢复就标记损坏,让页面给出恢复失败说明,而不是直接显示空白编辑器。

实际项目里,很多问题不是第一次进入页面暴露,而是在冷启动、返回前台、页面重建或回调晚到时暴露。把这段生命周期补上,修复才算闭环。

恢复提示要给用户选择

情况页面表现工程处理
找到稳定草稿提示恢复上次编辑用户确认后进入编辑页
只有损坏草稿隔离坏文件提示无法恢复并保留日志
附件缺失恢复正文并标记附件失败不要丢正文
保存中崩溃使用上一个 stabletemp 不覆盖正式草稿

兜底路径不是可有可无。用户遇到异常时,页面至少要给出当前状态、可执行动作和可排查线索。

排查时找直接覆盖草稿的代码

rg-n"draft.json|writeText|replace|autosave|DraftSnapshot"entry features common rg-n"JSON.parse|JSON.stringify|attachmentIds|DraftJournal"entry features common rg-n"onTextChange|onInput|schedule\(|debounce"entry features common

第一组命令找旧路径,第二组命令找新边界,第三组命令看生命周期或回调位置。命中后不要只看字段名,要判断它是否仍在直接改页面状态。

验证清单

验证项预期结果
输入后立刻杀进程重启恢复到上一份完整草稿
写 temp 失败stable 草稿不损坏
JSON 半截进入损坏隔离,不显示空白假象
附件缺失正文仍可恢复
连续输入不会每字符同步写文件卡顿

这些验证最好在真机或模拟器里按顺序走一遍。只看源码很难发现冷启动、重启和回调晚到这类问题。

常见问题和处理方式

现象常见原因处理方式
草稿半截直接覆盖正式文件temp 加替换
打开变空白解析失败当空草稿损坏隔离并提示
附件乱了正文和附件分开保存统一 DraftSnapshot
输入卡顿保存频率太高防抖保存

如果现象没有出现在表里,仍然建议按“输入来源 -> 中间状态 -> 持久化或页面展示 -> 失败兜底”的顺序排查。

小结:草稿保存先保护上一份完整内容

草稿不是普通缓存,它承载用户输入。写入时先生成完整快照,落临时文件,校验后替换正式版本;重启时按恢复账本加载稳定版本,坏文件隔离。这样即使崩溃发生在写入中间,用户也不会丢掉上一份可用内容。
| 保存频率太高 | 防抖保存 |

如果现象没有出现在表里,仍然建议按“输入来源 -> 中间状态 -> 持久化或页面展示 -> 失败兜底”的顺序排查。

小结:草稿保存先保护上一份完整内容

草稿不是普通缓存,它承载用户输入。写入时先生成完整快照,落临时文件,校验后替换正式版本;重启时按恢复账本加载稳定版本,坏文件隔离。这样即使崩溃发生在写入中间,用户也不会丢掉上一份可用内容。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/19 11:41:47

终极免费解决方案:Windows窗口强制调整工具WindowResizer完全指南

终极免费解决方案&#xff1a;Windows窗口强制调整工具WindowResizer完全指南 【免费下载链接】WindowResizer 一个可以强制调整应用程序窗口大小的工具 项目地址: https://gitcode.com/gh_mirrors/wi/WindowResizer WindowResizer是一款专门用于强制调整Windows应用程序…

作者头像 李华
网站建设 2026/7/19 11:41:43

physx-rs常见问题解答:新手到专家的必备知识库

physx-rs常见问题解答&#xff1a;新手到专家的必备知识库 【免费下载链接】physx-rs &#x1f3b3; Rust binding for NVIDIA PhysX &#x1f980; 项目地址: https://gitcode.com/gh_mirrors/ph/physx-rs physx-rs是一个为NVIDIA PhysX物理引擎提供Rust绑定的开源项目…

作者头像 李华
网站建设 2026/7/19 11:41:35

DDR PHY寄存器配置与延迟校准技术详解:从原理到AM64x/AM243x实战

1. 项目概述与核心价值在嵌入式系统&#xff0c;尤其是基于TI AM64x/AM243x这类高性能异构处理器的设计中&#xff0c;DDR内存子系统的稳定性和性能是决定整个系统成败的关键。我接触过不少项目&#xff0c;硬件板子回来后&#xff0c;软件团队最头疼的就是“内存调不通”。要么…

作者头像 李华
网站建设 2026/7/19 11:41:11

如何快速实现文献翻译:智能学术助手的完整秘籍

如何快速实现文献翻译&#xff1a;智能学术助手的完整秘籍 【免费下载链接】zotero-pdf-translate Translate PDF, EPub, webpage, metadata, annotations, notes to the target language. Support 20 translate services. 项目地址: https://gitcode.com/gh_mirrors/zo/zote…

作者头像 李华
网站建设 2026/7/19 11:40:34

深入解析C2000 ePWM时间基准与同步机制:从寄存器配置到多模块协同

1. 项目概述与核心价值如果你正在用TI的C2000系列DSP做电机控制、数字电源或者任何需要精确PWM&#xff08;脉冲宽度调制&#xff09;输出的项目&#xff0c;那么ePWM模块绝对是你绕不开的核心。很多人刚开始接触时&#xff0c;会觉得它寄存器多、概念复杂&#xff0c;光是时间…

作者头像 李华
网站建设 2026/7/19 11:40:25

3分钟永久激活:KMS_VL_ALL_AIO智能激活脚本完全指南

3分钟永久激活&#xff1a;KMS_VL_ALL_AIO智能激活脚本完全指南 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 当Windows系统提示"您的Windows许可证即将过期"或Office弹出激活警告时…

作者头像 李华