用 File.Copy
+ 时间戳命名实现最简版本快照
用户文件的历史版本,本质是「在关键操作前保存一份副本」。不依赖数据库或 Git,
File.Copy配合时间戳是最轻量、最可控的起点。
常见错误是直接覆盖原文件后才想起来备份——必须把备份逻辑放在写入动作之前。
每次保存前,先执行File.Copy(sourcePath, backupPath),其中
backupPath包含
DateTime.Now.ToString("yyyyMMdd_HHmmss")
避免用 DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")(路径非法字符),推荐用下划线分隔的紧凑格式
备份目录建议与原文件同级,如 "doc.txt"→
"doc.txt_20241015_142301.bak",便于用户手动识别和清理 注意
File.Copy默认不覆盖同名文件,需显式传
true参数:
File.Copy(src, dst, true)
用 Directory.GetFiles
按时间排序列出历史版本
用户要“查看历史”,核心是把一堆带时间戳的备份文件按修改时间倒序排列——但不能只靠文件名里的字符串排序,因为文件系统修改时间更可靠。
场景:用户点击「历史版本」按钮,弹出列表;此时若仅按文件名排序,可能因命名不规范(如手动生成的备份)导致错乱。
用Directory.GetFiles(backupDir, "doc.txt_*.bak")获取所有匹配备份 对结果用
OrderByDescending(f => File.GetLastWriteTime(f))排序,比解析文件名更鲁棒 若需显示版本号(v1/v2),不要硬编码计数,而是用
Array.IndexOf(sortedFiles, f) + 1动态生成 注意
File.GetLastWriteTime在某些网络驱动器上可能不准,本地磁盘无此问题
删除旧版本时别只看数量,要加时间兜底
只保留最近 10 个版本看似合理,但若用户连续编辑一小时,10 个版本可能全是 10 分钟内的,毫无回溯价值;反之,长期不编辑又会积累大量冗余文件。
错误做法:遍历后取
.Skip(10)就删——没考虑时间跨度。 建议双条件清理:保留「最近 10 个」且「7 天内」的版本,其余全部删除 用
File.GetLastWriteTime(f) 判断过期删除前务必检查
File.Exists(f),防止多线程下被其他操作提前删掉引发异常 避免在 UI 线程同步执行大批量
File.Delete,卡顿明显;可后台任务 + 进度提示
注意 FileStream
未释放导致备份失败
最常踩的坑:用户正在编辑的文件被程序独占打开(比如用
new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None)),此时
File.Copy直接抛
IOException:“进程无法访问该文件,因为它正由另一进程使用”。
这不是权限问题,是文件锁冲突。
备份前加try { File.Copy(...) } catch (IOException ex) when (ex.Message.Contains("being used")) { /* 提示用户稍后再试 */ }
更稳妥的做法:用 File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)打开源文件(确保其他程序也能读写),再复制 如果编辑器本身支持自动备份(如 VS Code 的 .swp),建议优先复用其机制,而非自己抢锁 Windows 上可临时用
robocopy /Z(断点续传模式)绕过部分锁,但 C# 内建 API 更可控
版本控制的复杂性不在存储,而在时机判断和资源竞争。什么时候该备份、什么时候不该动、备份失败后怎么降级——这些边界比代码本身更需要小心对待。
