Skip to content

实用的 MT Photos 数据恢复工具集,用于从 PostgreSQL 备份与 JSON 文件清单中定位、校验并还原原始文件结构。 A complete toolkit for restoring MT Photos data with path reconstruction and MD5 verification.

Notifications You must be signed in to change notification settings

fx-k/mt-photos-recovery-toolkit

Repository files navigation

MT Photos 数据恢复工具集(含 PostgreSQL 处理脚本)

关联博客文章:https://keke.su/posts/2025-10-09-new-1.html

👋 这是一套围绕 MT Photos 数据恢复的实战脚本与流程说明,目标是:

  • 从 PostgreSQL 导出的备份(.sql)与 file 表导出的 JSON 清单中,批量在多块磁盘上定位原始文件;
  • 按数据库中原有目录结构进行复制与还原;
  • 二次 MD5 校验确保数据无误;
  • 最终批量更新 SQL 备份中的路径(可在必要时连带更新 MD5)。

脚本均使用 Python 标准库,无第三方依赖,适用于 macOS/Linux/Windows(示例命令以 macOS 为例)。


目录


环境与准备

  • Python ≥ 3.8
  • 备份文件:
    • PostgreSQL 导出的 SQL 备份(例如 mtphotos_backup-20250301.sql
    • file 表导出的 JSON(例如 file-nas.json)。本工具以该 JSON 作为“权威清单”。
  • 若干待搜索的磁盘/目录(NAS、外接盘等)。
  • 目标恢复目录(建议单独磁盘/分区)。

强烈建议先对 JSON 和 SQL 文件做额外备份。脚本在关键步骤会自动生成 .bak 文件,但多一层手工备份更稳妥。


恢复总流程

  1. 建索引:对需要搜索的每个磁盘运行 build_index.py 生成索引 JSON。
  2. 找文件:使用 recover_files.py 结合清单与索引,按清单中的原始目录结构复制到指定恢复根目录,并把新路径回写到清单的 new_path
  3. 分析缺漏:运行 check_unfound_files.py 查看还未找到的条目。若存在,给其他磁盘建立索引,再次执行第 2 步,可多轮迭代直至尽可能找到全部文件。
  4. 重新校验 MD5:运行 verify_recovery.py 对已恢复文件进行 MD5 校验,补充 new_md5 并记录 md5_check 结果。
  5. 更新 SQL:使用 update_sql_backup.py 读取“权威清单”更新 SQL 备份中的文件/文件夹路径;在必要时同步更新文件 MD5。

步骤一:建立磁盘索引 build_index.py

递归扫描一个搜索目录,建立“文件名 → 所有可能路径列表”的索引。

用法:

python3 build_index.py \
  "/Volumes/NAS/photos" \
  "/Users/ke/Downloads/fromJsonLocateFile/file-nas-index.json"

输出结构示例(简化):

{
  "summary": {
    "scan_directory": "/Volumes/NAS/photos",
    "scan_date": "2025-10-11T12:34:56",
    "total_paths_found": 123456,
    "unique_filenames": 98765
  },
  "index": {
    "IMG_20180414_192153.jpg": [
      "/Volumes/NAS/photos/2018/04/IMG_20180414_192153.jpg",
      "/Volumes/Backup/phone/IMG_20180414_192153.jpg"
    ]
  }
}

提示:

  • 大目录扫描需要时间与内存,建议逐块磁盘/目录分别生成索引文件。
  • 后续找文件时可多次使用不同索引文件迭代恢复。

步骤二:根据索引批量恢复 recover_files.py

读取“权威清单”(file-nas.json 等)与索引 JSON,将命中的文件复制到“恢复根目录”下,目录结构尽量保持与清单 path 一致;把实际复制到的新路径写回清单 new_path

用法:

# 默认启用 MD5 校验(推荐)
python3 recover_files.py \
  "/Users/ke/Downloads/fromJsonLocateFile/file-nas.json" \
  "/Users/ke/Downloads/fromJsonLocateFile/file-nas-index.json" \
  "/Volumes/RECOVER/mtphotos_recovered"

# 若不执行精细定位,可临时关闭 MD5 校验。注意!这可能导致恢复的文件并非原文件。
python3 recover_files.py \
  "/Users/ke/Downloads/fromJsonLocateFile/file-nas.json" \
  "/Users/ke/Downloads/fromJsonLocateFile/file-nas-index.json" \
  "/Volumes/RECOVER/mtphotos_recovered" \
  --no-md5

行为要点:

  • 默认 MD5 校验:当清单中含有 md5 时,会对候选路径计算实际 MD5,确保内容一致再复制;若同名多处出现,仅择一并记录重复提示。
  • 关闭 MD5(--no-md5):仅按文件名命中后取第一个路径复制,速度快但有误判风险。脚本会在记录中添加 recovery_note,并建议后续务必跑第 4 步校验。如果您很确信文件名可以定位到唯一的文件,可以关闭MD5校验(这个使用场景,常用于文件在MT Photos服务端被修改导致MD5不一致,无法通过MD5校验一致时进行兜底)。
  • 目录重建:按清单 path 去掉 Windows 盘符后重建层级,最终落在你指定的恢复根目录内。
  • 日志:生成 recovery_log_YYYYmmdd_HHMMSS.txt,包含成功、重复、MD5 不一致、复制失败等分类明细。
  • 清单回写:成功复制后把 new_path 写回原清单(同时自动创建一次 .bak 备份)。

可多轮迭代:

  • 如果一次索引无法覆盖所有来源(多块磁盘),先用 A 盘索引恢复一轮,再用 B 盘索引继续恢复,清单会被逐步补齐 new_path

步骤三:分析未恢复项 check_unfound_files.py

统计清单中仍缺少 new_path 的记录,便于针对性补建索引再找文件。

用法:

python3 check_unfound_files.py \
  "/Users/ke/Downloads/fromJsonLocateFile/file-nas.json"

输出会列出未恢复文件名与原始路径,并给出合计数量。


步骤四:二次 MD5 校验 verify_recovery.py

对已恢复文件执行 MD5 校验,确保文件与数据库原记录一致;若原清单无 MD5,则生成 new_md5 供后续 SQL 更新使用。

用法:

python3 verify_recovery.py \
  "/Users/ke/Downloads/fromJsonLocateFile/file-nas.json"

行为要点:

  • 会先对清单生成 *.bak_verify_YYYYmmddHHMMSS 备份。
  • 对每条含 new_path 的记录:
    • 若原始 md5 存在:计算 new_md5 并与之比对,在 md5_check 标记 True/False;异常记录为 error_*
    • 若原始 md5 缺失:跳过比对,标记 md5_check = "skipped_no_original_md5",同时写入 new_md5(供后续 SQL 更新)。
  • 结果写回原清单。

步骤五:批量更新 SQL 路径 update_sql_backup.py

用“权威清单”(含 new_pathnew_md5md5_check)批量更新 PostgreSQL 备份 SQL 中的路径;必要时同步更新 MD5。

支持以下表与场景:

  • public.file:更新文件路径;当 md5_check 为 False 且存在 new_md5 时,同步更新 MD5。
  • public.folder:既包含文件也包含文件夹路径:
    • 通过 name 是否包含 . 粗分“文件/文件夹”场景;
    • 文件路径按文件映射更新;
    • 文件夹路径使用“最长前缀匹配”的目录映射批量更新子层级。
  • public.album_link:当 type='folder' 时,更新 JSON value.label 中的文件夹路径。

用法:

python3 update_sql_backup.py \
  "/Users/ke/Downloads/fromJsonLocateFile/file-nas.json" \
  "/Users/ke/Downloads/fromJsonLocateFile/mtphotos_backup-20250301.sql" \
  "/Users/ke/Downloads/fromJsonLocateFile/mtphotos_backup-20250301.updated.sql"

处理说明:

  • 同时支持 INSERT INTO ... VALUES (...)COPY ... FROM stdin; 两种导出形式。
  • public.file
    • name 一致且旧 MD5(或备选 new_md5)与记录匹配,且 md5_check 为 True,则仅更新路径;
    • md5_check 为 False 且 new_md5 存在,则同时更新路径与 MD5。
  • public.folderpublic.album_link:采用目录映射与最长前缀规则,尽量保持原有子目录层级不变,仅替换根前缀。
  • 结果输出到新的 .updated.sql,原 SQL 不会被覆盖。

JSON 清单示例与字段说明

典型的 file 表导出(简化示例):

[
  {
    "id": 123,
    "name": "IMG_20180414_192153.jpg",
    "path": "/Volumes/old/phone/2018/IMG_20180414_192153.jpg",
    "md5": "17f6e7...",
    "new_path": "/Volumes/RECOVER/mtphotos_recovered/phone/2018/IMG_20180414_192153.jpg",
    "new_md5": "17f6e7...",
    "md5_check": true
  }
]
  • 必需字段namepath;若存在 md5 则可提升匹配准确度。
  • 恢复写回字段
    • new_pathrecover_files.py 成功复制后写入。
    • new_md5md5_checkverify_recovery.py 写入。

重要说明与常见问题

  • 建议逐盘建索引:索引结构为“文件名 → 路径列表”,体量大时内存占用较高。拆分为多份索引文件,分轮恢复更稳妥。
  • 关于 MD5
    • 恢复阶段若关闭 MD5,只能保证“文件名命中”,存在误匹配风险。请务必执行第 4 步的 MD5 校验。
    • SQL 更新时,只有在 md5_check=False 且清单提供 new_md5 时才会替换 MD5;否则保留原值。
  • 路径规范化
    • 脚本会去掉 C: 这类 Windows 盘符并统一使用 /,以便跨平台处理;
    • 恢复时会尽量复刻清单 path 的相对目录层级。
  • SQL 解析边界
    • 假设 INSERT INTO ... VALUES (...) 为单行(可包含多条 (...));
    • COPY 行按制表符切分并遵循 PostgreSQL 文本 COPY 的转义规则;
    • 若你的导出格式非常定制(多行 VALUES、特殊分隔),建议先在样本文件上试跑并审阅 .updated.sql
  • 日志与备份
    • recover_files.py 会生成按类目分组的恢复日志;
    • verify_recovery.pyrecover_files.py 会对清单做备份;
    • update_sql_backup.py 只写入新的输出 SQL,不会覆盖原始备份。
  • 多轮恢复策略
    1. 用 A 盘索引跑一轮恢复;
    2. check_unfound_files.py 看还差哪些;
    3. 为 B 盘建索引继续恢复;
    4. 反复迭代直至尽可能全部补齐;
    5. 跑 MD5 校验与 SQL 更新。

致谢与许可证

  • 代码以 MIT License 开源,欢迎提交 PR 改进更多表与字段的适配。
  • 感谢MT Photos官方文档提供的灵感与最佳实践。

About

实用的 MT Photos 数据恢复工具集,用于从 PostgreSQL 备份与 JSON 文件清单中定位、校验并还原原始文件结构。 A complete toolkit for restoring MT Photos data with path reconstruction and MD5 verification.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages