最简快照方案是File.Copy配合时间戳命名备份,备份必须在写入前执行,路径用yyyyMMdd_HHmmss格式,备份名如“doc.txt_20241015_142301.bak”,需传true覆盖;历史列表按File.GetLastWriteTime倒序排列更可靠;清理应双条件:保留最近10个且7天内的版本。

用 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 分钟内的,毫无回溯价值;反之,长期不编辑又会积累大量冗余文件。
方科网络ERP图文店II版为仿代码站独立研发的网络版ERP销售程序。本本版本为方科网络ERP图文店版的简化版,去除了部分不同用的功能,使得系统更加精炼实用。考虑到图文店的特殊情况,本系统并未制作出入库功能,而是将销售作为重头,使用本系统,可以有效解决大型图文店员工多,换班数量多,订单混杂不清的情况。下单、取件、结算分别记录操作人员,真正做到订单全程跟踪!无限用户级别,不同的用户级别可以设置不同的价
错误做法:遍历后取 .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 更可控
版本控制的复杂性不在存储,而在时机判断和资源竞争。什么时候该备份、什么时候不该动、备份失败后怎么降级——这些边界比代码本身更需要小心对待。









