我给自己写了一个本地文件夹同步备份工具:先预览,再增量复制

举报
yd_294377644 发表于 2026/11/06 16:52:55 2026/11/06
【摘要】 项目资料和工作文档散落在不同目录里,手动整包复制既慢又容易漏,直接使用镜像同步又担心误删扩散。于是我用 Python 写了一个本地文件夹同步备份工具,支持差异预览、增量复制、版本保留和结果校验。这篇博客记录同步计划生成、安全写入、清单管理与恢复验证中的实践经验。

目录

  1. 缘起:复制整个文件夹为什么仍然不放心
  2. 需求拆解:同步和备份不是一回事
  3. 技术选型:标准库就能完成增量备份
  4. 核心实现一:生成可检查的同步计划
  5. 核心实现二:安全复制与旧版本保留
  6. UI 定制:任何写入都先展示影响范围
  7. 三个备份工具不能回避的坑
  8. 结尾:没有验证过的备份,只是一份希望

1. 缘起:复制整个文件夹为什么仍然不放心

我有几个目录需要定期备份:正在开发的项目、合同附件、博客素材和一些不适合上传云盘的工作文件。最开始的办法很朴素——隔一段时间把整个文件夹复制到移动硬盘。

这个方法用久后,问题越来越明显:

  • 文件夹很大,即使只改了几个文件也要重新复制很久;
  • 中途断开移动硬盘,不知道哪些文件已经完成;
  • 同名目录越来越多,很难确认哪份是最新版本;
  • 手动选择文件时可能漏掉新增的子目录;
  • 如果使用镜像命令,源目录误删也可能同步删除备份。

我真正需要的不是双向同步,也不是实时监控,而是一个明确的单向流程:比较源目录和备份目录,列出即将新增或更新的文件,确认后执行,并保留可核对的记录。

2. 需求拆解:同步和备份不是一回事

在开始写代码前,我先划清“同步”和“备份”的边界。同步追求两边状态一致,备份更关心旧数据能否恢复。我的工具以备份为目标,因此默认不会删除目标端多出的文件。

诉求(人话) 拆解后(可验收)
只复制变化内容 比较相对路径、大小和修改时间
执行前知道会发生什么 生成新增、更新、跳过和冲突清单
原有备份不要被直接覆盖 更新前将旧文件移动到版本目录
中断后能继续 每个文件独立完成并记录结果
复制完确认内容正确 可选执行 SHA-256 校验
不让误删传到备份 默认永不删除目标端文件

第一版不做双向合并。双向同步需要解决两边同时修改、删除传播和冲突选择,规则复杂得多,也更容易让“备份工具”变成“误操作放大器”。

心得:工具越接近数据安全,默认行为越应该保守。

3. 技术选型:标准库就能完成增量备份

这个工具主要处理本地文件系统,Python 标准库已经覆盖所需能力:

能力 方案
遍历目录 pathlib.Path.rglob()
复制文件和元数据 shutil.copy2()
内容校验 hashlib.sha256()
原子替换 os.replace()
备份清单 JSON Lines
桌面界面 tkinter

我没有引入数据库。每次执行生成一份只追加的 JSON Lines 日志,一行对应一个文件结果。即使程序意外退出,已写入的记录仍然可以逐行读取,不会因为最后一个 JSON 数组没有闭合而整份损坏。

复制任务默认串行执行。备份目标经常是移动硬盘或机械硬盘,并行读取和写入不一定更快,还会让进度和错误恢复变复杂。稳定比跑满磁盘更重要。

4. 核心实现一:生成可检查的同步计划

同步计划以源目录中的相对路径为核心。无论源目录放在哪里,同一个文件都能映射到目标目录中的确定位置。

from dataclasses import dataclass
from pathlib import Path

@dataclass
class PlanItem:
    action: str
    source: Path
    target: Path
    relative: Path
    size: int

def build_target(source_file, source_root, target_root):
    relative = source_file.relative_to(source_root)
    return relative, target_root / relative

比较时先使用文件大小和纳秒级修改时间。目标不存在就是新增;大小不同就是更新;两者相同则先视为未变化。

def decide_action(source, target):
    if not target.exists():
        return "add"

    source_stat = source.stat()
    target_stat = target.stat()

    if source_stat.st_size != target_stat.st_size:
        return "update"

    if source_stat.st_mtime_ns != target_stat.st_mtime_ns:
        return "verify"

    return "skip"

verify 项会进一步计算哈希。如果内容相同,只修正清单状态,不重复复制;如果哈希不同,才进入更新列表。

扫描完成后,计划会汇总文件数量、预计复制字节数和目标端已有但源端不存在的文件。最后这一类只标记为“目标独有”,不会自动删除。

心得:先把文件变化变成一份静态计划,执行过程才有依据,也更容易复核。

5. 核心实现二:安全复制与旧版本保留

直接把源文件复制到最终目标有一个问题:写到一半断电,目标路径下会留下看似正常、实际不完整的文件。我的做法是先写同目录临时文件,成功后再替换。

import os
import shutil
import uuid

def copy_safely(source, target):
    target.parent.mkdir(parents=True, exist_ok=True)
    temp = target.with_name(f".{target.name}.{uuid.uuid4().hex}.tmp")

    try:
        shutil.copy2(source, temp)
        os.replace(temp, target)
    finally:
        if temp.exists():
            temp.unlink()

如果目标文件已经存在,程序不会立刻覆盖。它会先将旧版本移动到目标根目录下的版本区:

.backup_versions/2026-11-06_103500/文档/合同.docx

版本目录保留原始相对路径,恢复时不会丢失文件位置。只有旧版本移动成功后,新文件才开始写入。

可选的严格模式会在复制后分别计算源文件和目标文件的 SHA-256。校验失败时,新文件被标记为不可用,旧版本仍然保留。对大文件来说校验会增加一次完整读取,所以界面会明确展示时间成本。

6. UI 定制:任何写入都先展示影响范围

界面顶部只需要选择源目录和目标目录。点击“扫描差异”后,中间表格展示完整计划:

操作 | 相对路径 | 文件大小 | 原因 | 目标状态

新增使用绿色,更新使用橙色,冲突使用红色,跳过项默认折叠。用户可以按操作类型筛选,也能打开源文件和目标文件所在目录进行人工确认。

开始备份前,界面再次显示四个数字:新增数量、更新数量、预计写入体积和将保留的旧版本数量。只要源目录或目标目录在扫描后发生变化,原计划就会失效,必须重新扫描。

执行过程中,每完成一个文件就写一条日志。停止按钮只阻止后续文件开始,不会强行中断当前复制。结束页面会列出成功、失败和未执行项,并提供重新执行失败项的入口。

我没有加入“保持两边完全一致”的按钮。如果以后真的需要镜像模式,它也必须作为独立功能出现,并对删除列表进行单独确认。

7. 三个备份工具不能回避的坑

坑一:修改时间不是绝对可靠的内容证明。 不同文件系统的时间精度不同,复制工具也可能保留旧时间。大小和时间适合快速筛选,但对可疑项目仍要用哈希确认,严格模式则对所有实际复制文件做校验。

坑二:符号链接可能把扫描带到源目录之外。 一个链接可能指向上级目录、网络路径,甚至形成循环。工具默认只记录符号链接本身,不跟随目录链接;如果无法按当前平台安全复制,就明确跳过并报告。

坑三:能复制成功不等于能恢复。 备份目录中有文件,只能说明写入发生过。定期从备份中抽取文件、校验哈希并尝试打开,才能验证恢复链路。工具因此提供“只校验不复制”模式,用清单重新检查现有备份。

还有一个必须明确的事实:如果源目录和备份目录位于同一块物理硬盘,硬盘损坏时两份数据可能同时丢失。工具会提示这种情况,但不会假装同盘副本等同于真正备份。

8. 结尾:没有验证过的备份,只是一份希望

完成这个工具后,我不再定期复制整个大目录,也不会因为害怕镜像删除而一直拖着不备份。每次操作都从一份差异计划开始,哪些文件会新增、哪些会更新、旧版本保存在哪里,都能提前看到。

一个让我放心的本地备份流程包含五件事:

  1. 明确数据只从源目录流向目标目录;
  2. 写入之前展示完整计划;
  3. 更新文件之前保留旧版本;
  4. 中断后能识别已完成和未完成项目;
  5. 定期校验并实际尝试恢复。

如果你也想做类似工具,可以先实现单向新增复制和预览,不要一开始就加入删除、双向合并与实时监控。备份工具最可贵的能力不是自动化程度,而是每一步都可解释、可停止、可恢复。

当备份结果能够被验证,它才不再是一份心理安慰,而是真正可用的数据保障。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。