跳过正文
  1. 文章/

Synology Photos 迁移到 Immich 操作手册

·607 字·3 分钟
作者
Yang Hu

将家庭相册从 Synology Photos 迁移到自托管 Immich 实例的个人操作手册。 涵盖批量上传、Google Takeout 导入,以及通过 Synology PostgreSQL 数据库重建相册。

环境说明
#

  • 来源:运行 Synology Photos 的 Synology NAS(多用户)
  • 目标:同一 NAS 上自托管的 Immich
  • 上传工具immich-go v0.31+
  • 客户端:Windows 上的 WSL2,SSH 访问 NAS
  • 相册脚本:自定义 Python(migrate_albums.py),使用 Immich REST API

第一阶段:照片上传
#

策略
#

每位用户有两个来源:

  1. Google Takeout — 截止日期之前的照片(以 Google Photos 为主要存储时)
  2. Synology 文件夹 — 截止日期之后的照片(切换到 Synology 作为主存储后)

截止日期即你从 Google Photos 切换到 Synology 作为主要相册存储的时间点。此前的照片以完整分辨率存在 Google Photos 中;此后的原始分辨率在 Synology 上。

immich-go 文件夹上传
#

直接在 NAS 上运行 — 避免将大型相册(100 GB+)通过网络传输。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
ssh nas
cd /path/to/immich-docker

nohup ./immich-go upload from-folder \
  --server=http://<nas-ip>:2283 \
  --api-key=<user-api-key> \
  --concurrent-tasks=6 \
  --manage-raw-jpeg=StackCoverJPG \
  --manage-burst=Stack \
  --pause-immich-jobs=true \
  --session-tag \
  --on-errors=continue \
  /path/to/photos \
  > upload.log 2>&1 &

关键参数:

参数用途
--manage-raw-jpeg=StackCoverJPG将 RAW+JPEG 对堆叠,以 JPEG 为封面
--manage-burst=Stack堆叠连拍序列
--pause-immich-jobs=true上传期间暂停 ML 任务(更快)
--session-tag用会话 ID 标记所有上传,便于追踪
--concurrent-tasks=6并发数 — 根据 NAS CPU 调整
--on-errors=continue单文件失败时不中止整体

将文件夹直接作为相册上传:

1
2
3
4
5
6
./immich-go upload from-folder \
  --server=http://<nas-ip>:2283 \
  --api-key=<user-api-key> \
  --into-album="Album Name" \
  /path/to/folder \
  > upload.log 2>&1 &

Google Takeout 导入
#

下载 Takeout 归档后:

1
2
3
4
5
./immich-go upload from-google-photos \
  --server=http://<nas-ip>:2283 \
  --api-key=<user-api-key> \
  --create-albums \
  /path/to/takeout-*.zip

使用 --date-range=<start>,<cutoff> 限制截止日期之前的照片(避免导入已在 Synology 上存有原始分辨率的 Google 压缩版本)。

第二阶段:重建相册
#

Synology Photos 将相册成员关系存储在 PostgreSQL 数据库中。照片上传到 Immich 后,通过读取 Synology 数据库 TSV 导出的脚本,利用 Immich REST API 重建相册。

从 Synology 数据库导出相册数据
#

Synology Photos 使用 PostgreSQL,数据库名为 synofoto。必须以 postgres 用户运行(peer 认证 — 无密码,但需要 sudo)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 在 NAS 终端执行:
sudo su -s /bin/sh postgres -c "psql synofoto -A -F$'\t' -t -c \"
SELECT a.id, a.name, a.shared, ui.name, u.id_user, ui2.name,
  f.name, u.filename, u.takentime, u.duplicate_hash
FROM public.album a
JOIN public.normal_album na ON na.id = a.id
JOIN public.many_item_has_many_normal_album mia ON mia.id_normal_album = na.id
JOIN public.item i ON i.id = mia.id_item
JOIN public.unit u ON u.id_item = i.id
JOIN public.folder f ON f.id = u.id_folder
JOIN public.user_info ui ON ui.id = a.id_user
JOIN public.user_info ui2 ON ui2.id = u.id_user
ORDER BY a.id, f.name, u.filename;
\"" > /volume1/homes/<admin-user>/album_export.tsv

# 复制到工作站:
scp nas:/volume1/homes/<admin-user>/album_export.tsv ./album_export.tsv

注意: Synology 上的旧版 psql 不支持 --csv,使用 -A -F$'\t' -t 输出 TSV 格式。

关键数据表:

用途
album相册元数据(名称、所有者、共享标志)
normal_album标记为普通(非智能)类型的相册
many_item_has_many_normal_album相册 ↔ 照片成员关系
item照片条目(逻辑层,unit 的父级)
unit物理文件(文件名、拍摄时间、文件夹外键)
folder文件夹路径
user_info用户显示名称

从数据库字段构建物理路径:

  • 个人空间:/volume1/homes/<username>/Photos<folder_path>/<filename>
  • 共享空间:/volume1/photo<folder_path>/<filename>

TSV 字段(10 列,制表符分隔):

1
album_id  album_name  shared  owner  file_owner_id  file_owner  folder_path  filename  takentime  duplicate_hash

相册迁移脚本
#

migrate_albums.py 读取 TSV 并在 Immich 中重建所有相册:

  1. 对每张照片,调用 POST /api/search/metadata,参数为 originalFileName
  2. 如有多个文件名匹配,选择 takentime 最接近的(Synology 数据库的 Unix 时间戳)
  3. 以相册所有者账号通过 POST /api/albums 创建相册
  4. 通过 PUT /api/albums/{id}/assets 批量(每批 100 个)添加资产
  5. 通过 PUT /api/albums/{id}/users 将相册共享给家庭成员(editor 角色)

脚本顶部配置块:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
IMMICH_URL = "http://<nas-ip>:2283"

API_KEYS = {
    "user1": "<api-key>",   # 从 Immich UI → 头像 → 账户设置 → API Keys 获取
    "user2": "<api-key>",
}

IMMICH_USER_IDS = {
    "user1": "<immich-uuid>",   # 从 GET /api/users 或 Immich 管理面板获取
    "user2": "<immich-uuid>",
}

用法:

1
2
3
4
5
6
7
# 预演 — 安全,不做任何修改
python3 migrate_albums.py --dry-run
python3 migrate_albums.py --dry-run --album "My Album"

# 执行
python3 migrate_albums.py --album "My Album"
python3 migrate_albums.py

重要说明:

  • 脚本在创建前检查相册名称是否已存在 — 重复运行安全,但如果绕过检查会创建重复项
  • 没有配置 API key 的照片所有者会被跳过并报告;补充 key 后重新运行即可
  • 未匹配的照片(尚未上传)会被打印出来,但不阻塞其他相册的处理
  • 搜索按 Immich 用户隔离,因此照片查找必须使用文件所有者的 API key,而非相册所有者的

验证
#

迁移完成后,对比 Synology 和 Immich 之间的相册照片数量: 在 Synology 数据库中查询每个相册的 COUNT(*),与 GET /api/albums/{id} 返回的 assetCount 字段对比。

经验总结
#

  • 在 NAS 上运行 immich-go — 无网络瓶颈;对 100 GB+ 的相册库至关重要
  • 上传期间暂停 Immich ML 任务--pause-immich-jobs=true 显著提升导入速度
  • immich-go 自动处理重复 — 重复运行安全;已上传的文件会被检测并跳过
  • 每个 Immich 用户需要独立的 API key — 相册成员搜索必须使用文件所有者的 key(搜索结果按用户隔离)
  • 文件名 + 拍摄时间匹配可靠originalFileName 搜索加 Unix 时间戳消歧,对 Synology Photos 数据集效果很好
  • Synology 上的旧版 psql — 没有 --csv 参数;使用 -A -F$'\t' -t 输出 TSV
  • immich-go 的 --session-tag — 用会话 ID 标记所有上传,便于审计哪些文件来自哪次运行
  • 相册脚本幂等性 — 创建前检查已有相册名称;未匹配的照片不阻塞其他处理

参考资料
#