判断文件是否需要备份需采用修改时间与MD5双校验:先比对四舍五入至毫秒级的os.path.getmtime(),时间不同或目标不存在则触发备份;时间相同再比大小,不等则备份;均一致时才计算MD5校验,确保准确又高效。

如何判断文件是否需要备份:修改时间 + MD5 双校验
只比对修改时间(os.path.getmtime())不可靠——比如文件被复制、移动或时钟回拨,会导致时间戳异常;只比对 MD5 又太慢,尤其对大文件。稳妥做法是先快速比对时间戳,仅当时间不同或目标无记录时,再计算并比对 md5 值。
关键逻辑是:若本地文件修改时间 > 备份目录中同名文件的修改时间,则触发 MD5 校验;若时间相同,直接跳过;若备份中不存在该文件,必须备份。
- 用
os.path.getmtime(path)获取浮点型时间戳(秒级精度),注意 Windows 和 Linux 下精度可能有微小差异,建议用round(..., 3)统一保留毫秒级比较 - MD5 计算前先检查文件大小是否一致(
os.path.getsize()),大小不同直接跳过哈希计算,省下大量 I/O - 读取大文件时务必用分块(如
64 * 1024字节/次),避免MemoryError
Python 实现增量备份的核心函数结构
不依赖第三方库,纯标准库即可完成。核心是维护一个轻量元数据记录(例如 JSON 文件),存每个已备份文件的 mtime、size 和 md5(可选)。
import json
import os
import hashlib
import shutil
<p>def calc_md5(filepath, chunk_size=65536):
h = hashlib.md5()
with open(filepath, "rb") as f:
while chunk := f.read(chunk_size):
h.update(chunk)
return h.hexdigest()</p><p>def should_backup(src, dst, meta_dict):
if not os.path.exists(dst):
return True
src_mtime = round(os.path.getmtime(src), 3)
dst_mtime = round(os.path.getmtime(dst), 3)
if src_mtime != dst_mtime:
return True
if os.path.getsize(src) != os.path.getsize(dst):
return True</p><h1>时间和大小都一致,但保险起见仍校验 MD5(仅当 meta 中有记录且匹配)</h1><pre class="brush:php;toolbar:false;">if src in meta_dict and meta_dict[src].get("md5"):
return calc_md5(src) != meta_dict[src]["md5"]
return False
注意:meta_dict 是内存中加载的字典,键为源文件绝对路径(或相对路径,需统一规范),值为 {"mtime": ..., "size": ..., "md5": ...}。每次备份后要更新并写回 JSON 文件。
备份时如何安全覆盖与保留旧版本
直接 shutil.copy2(src, dst) 会覆盖目标文件,一旦出错无法回退。生产级增量备份应默认保留上一版(如加 .prev 后缀)或使用原子写入。
- 推荐方式:先
shutil.copy2(src, dst + ".tmp"),再os.replace(dst + ".tmp", dst)——os.replace在同一文件系统下是原子操作,不会出现中间态损坏 - 若需保留历史版本,不要覆盖,而是按时间戳生成新文件名,例如
f"{dst}.{int(time.time())}",再软链dst指向最新版 - 备份前检查磁盘空间:
shutil.disk_usage(os.path.dirname(dst)),剩余空间小于源文件 2 倍时中止,防止写一半失败
常见错误与静默失败点
这类脚本最容易“看似运行成功,实则漏备”——因为错误被吞掉或条件判断写反。
-
OSError: [Errno 2] No such file or directory:没检查os.path.exists(src)就直接调getmtime,循环遍历时某个文件已被删除 - 硬链接文件的
mtime和普通文件行为一致,但md5相同,不影响逻辑;符号链接需用os.path.islink()判断,决定是否跟随(shutil.copy2默认不跟随) - Windows 下路径大小写不敏感,但 Python 的字典 key 是区分大小写的,若用路径作 key,需统一转小写(
os.path.normcase()) - JSON 元数据文件本身被误删或损坏,导致所有文件都被重备——应在每次读取后验证字段完整性,缺失关键字段(如
"mtime")则视为首次备份
最易被忽略的是时区与夏令时:os.path.getmtime() 返回的是本地时间戳,跨时区同步时务必确认两端系统时钟已用 NTP 对齐,否则时间比较完全失效。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











