Immich CLI 实战指南:认证、上传与自动化管理自托管照片库的完整命令参考
【免费下载链接】immichHigh performance self-hosted photo and video management solution.项目地址: https://gitcode.com/GitHub_Trending/im/immich
Immich 除了 Web 端与移动端,还提供官方的命令行工具@immich/cli,用于在终端中向 Immich 服务器批量上传照片和视频、检查服务器版本与统计信息。本文以官方功能文档 command-line-interface.md 为主体,完整覆盖安装、认证、upload命令全部选项与环境变量,并结合 packages/cli 目录下的源码,深入讲解去重、并发上传、XMP sidecar 附带上传、watch 监听等底层实现细节,帮助你在服务器、NAS 脚本或 CI 环境中把 CLI 真正用起来。
功能定位与适用场景
官方文档将 CLI 的当前能力概括为两点:
- 将照片和视频上传到 Immich;
- 查看服务器版本信息。
文档同时注明更多功能在规划中。如果你的目标是批量导入 Google Photos Takeout 导出的目录,官方文档建议使用社区维护的工具 immich-go;而 CLI 更适合"日常把某个目录同步/归档进 Immich"这类场景,因为它支持 dry-run、并发控制、JSON 输出等面向脚本化的设计。
CLI 源码位于 packages/cli 目录,包名为@immich/cli,入口命令注册在 src/index.ts,package.json中bin字段将immich命令映射到构建产物(见 packages/cli/package.json)。
环境要求与安装
要求
- Node.js 22 及以上(packages/cli/package.json 中
engines声明为node >=22.0.0); - Npm。
如果系统无法安装 Node/npm,可以使用官方提供的 Docker 版本(见下文)。
通过 NPM 安装
npm i -g @immich/cli如果你安装过旧版(legacy)CLI,需要先卸载:
npm uninstall -g immich通过 Docker 运行
当 npm 不可用时,可直接运行官方 CLI 镜像。docker run命令会直接在容器内执行immich命令,因此可以把upload等参数直接追加在命令行末尾:
docker run -it -v "$(pwd)":/import:ro -e IMMICH_INSTANCE_URL=https://your-immich-instance/api -e IMMICH_API_KEY=your-api-key ghcr.io/immich-app/immich-cli:latest例如执行递归上传:
docker run -it -v "$(pwd)":/import:ro -e IMMICH_INSTANCE_URL=https://your-immich-instance/api -e IMMICH_API_KEY=your-api-key ghcr.io/immich-app/immich-cli:latest upload -a -c 5 --recursive directory/请根据实际环境修改IMMICH_INSTANCE_URL和IMMICH_API_KEY两个环境变量;也可以改用 Docker env file 来存放敏感的 API key。
从 packages/cli/Dockerfile 可以确认镜像的工作目录被设置为/import(WORKDIR /import),所以-v "$(pwd)":/import:ro把宿主机当前目录以只读方式挂载进去,容器内的.即指向待上传目录;ENTRYPOINT直接执行 CLI 的构建产物,参数透传给immich命令。
命令总览(Usage)
运行immich无参数时输出的完整帮助如下(与官方文档一致):
$ immich Usage: immich [options] [command] Command line interface for Immich Options: -V, --version output the version number -d, --config-directory <directory> Configuration directory where auth.yml will be stored (default: "~/.config/immich/", env: IMMICH_CONFIG_DIR) -u, --url [url] Immich server URL (env: IMMICH_INSTANCE_URL) -k, --key [key] Immich API key (env: IMMICH_API_KEY) -h, --help display help for command Commands: login|login-key <url> <key> Login using an API key logout Remove stored credentials server-info Display server information upload [options] [paths...] Upload assets help [command] display help for command对照 src/index.ts 可以看到,这四个全局选项(-d/-u/-k等)都通过 commander 的Option.env()绑定了环境变量,因此所有选项都优先取命令行参数,其次回退到同名环境变量。
| 全局选项 | 环境变量 | 默认值 | 说明 |
|---|---|---|---|
-V, --version | — | — | 输出版本号 |
-d, --config-directory <dir> | IMMICH_CONFIG_DIR | ~/.config/immich/ | 存放auth.yml的凭证目录 |
-u, --url <url> | IMMICH_INSTANCE_URL | — | Immich 服务器 URL(以/api结尾) |
-k, --key <key> | IMMICH_API_KEY | — | Immich API key |
认证机制:login / logout 与 auth.yml
获取 API Key
API key 在 Web 界面的用户设置面板中获取,可以为 key 指定权限以限制其访问范围。
login 命令
# immich login [url] [key] immich login http://192.168.1.216:2283/api HFEJ38DNSDUEGlogin成功后会把凭证写入配置目录下的auth.yml文件,默认目录为~/.config/immich/;目录可以用-d选项或环境变量IMMICH_CONFIG_DIR指定。请妥善保管该文件——用完执行logout,或手动删除它。
从 src/commands/auth.ts 的实现可以看到login的完整流程:
- 调用
connect(url, key)发起连接。connect位于 src/utils.ts,它还会先请求<url>/.well-known/immich端点做服务发现:如果服务器返回了 API 端点,CLI 会自动把 URL 纠正为端点地址,因此即使 URL 写得略有偏差也能连上; - 通过
requirePermissions([Permission.UserRead])校验当前 key 是否具备UserRead权限,缺失时打印缺失的权限名并退出(process.exit(1)); - 调用
getMyUser()确认身份,打印Logged in as <email>; - 若配置目录不存在则递归创建,最后以
0o600(仅属主可读写)的文件权限写入auth.yml(见 src/utils.ts 的writeAuthFile),降低凭证被同机其他用户读取的风险。
logout 命令
immich logout实现为直接删除auth.yml文件(src/commands/auth.ts)。
认证的回退顺序
执行任何需要认证的命令时,src/utils.ts 中的authenticate按以下顺序取凭证:
- 命令行同时提供
-u和-k时直接使用它们(不读 auth 文件); - 否则读取配置目录中的
auth.yml。若文件不存在则提示No auth file exists. Please login first.并退出。
这意味着login并不是每次上传的强制前置步骤——你完全可以每次都用-u/-k或IMMICH_INSTANCE_URL/IMMICH_API_KEY环境变量传入凭证,这正是 Docker 用法的工作方式。
upload 命令:完整选项参考
官方文档中upload子命令的完整帮助输出:
Usage: immich upload [paths...] [options] Upload assets Arguments: paths One or more paths to assets to be uploaded Options: -r, --recursive Recursive (default: false, env: IMMICH_RECURSIVE) -i, --ignore <pattern> Pattern to ignore (env: IMMICH_IGNORE_PATHS) -h, --skip-hash Don't hash files before upload (default: false, env: IMMICH_SKIP_HASH) -H, --include-hidden Include hidden folders (default: false, env: IMMICH_INCLUDE_HIDDEN) -a, --album Automatically create albums based on folder name (default: false, env: IMMICH_AUTO_CREATE_ALBUM) -A, --album-name <name> Add all assets to specified album (env: IMMICH_ALBUM_NAME) --visibility <visibility> Set the visibility of uploaded assets (choices: "archive", "timeline", "hidden", "locked", env: IMMICH_VISIBILITY) -n, --dry-run Don't perform any actions, just show what will be done (default: false, env: IMMICH_DRY_RUN) -c, --concurrency <number> Number of assets to upload at the same time (default: 4, env: IMMICH_UPLOAD_CONCURRENCY) -j, --json-output Output detailed information in json format (default: false, env: IMMICH_JSON_OUTPUT) --delete Delete local assets after upload (env: IMMICH_DELETE_ASSETS) --delete-duplicates Delete local assets that are duplicates (already exist on server) (env: IMMICH_DELETE_DUPLICATES) --no-progress Hide progress bars (env: IMMICH_PROGRESS_BAR) --watch Watch for changes and upload automatically (default: false, env: IMMICH_WATCH_CHANGES) --help display help for command官方文档特别说明:以上所有选项同样可以从环境变量读取,这对 Docker 场景(用--env-file注入配置)和 cron 脚本尤其有用。对照 src/index.ts 的注册代码,还可以补充两个源码级约束:
-A, --album-name与-a, --album互斥(.conflicts('album')),同时指定会报错;-n, --dry-run与--skip-hash互斥(.conflicts('skipHash'))——dry-run 依赖哈希检查来判断哪些文件"将被上传",因此二者不能同时使用;--watch会自动隐含progress: false(.implies({ progress: false })),因为进度条渲染与监听日志输出会互相干扰。
关于并发默认值,有一个值得注意的细节:文档的帮助输出标注default: 4,而从 src/index.ts 的当前源码看,默认值是按 CPU 核数动态计算的Math.max(1, os.cpus().length - 1)。以你所运行版本的immich upload --help实际输出为准。
选项速查表
| 选项 | 环境变量 | 默认值 | 作用 |
|---|---|---|---|
-r, --recursive | IMMICH_RECURSIVE | false | 递归扫描子目录 |
-i, --ignore <pattern> | IMMICH_IGNORE_PATHS | — | 忽略匹配 glob 模式的文件,可多次指定 |
--skip-hash | IMMICH_SKIP_HASH | false | 上传前不计算文件哈希(提速用) |
-H, --include-hidden | IMMICH_INCLUDE_HIDDEN | false | 包含隐藏文件/目录 |
-a, --album | IMMICH_AUTO_CREATE_ALBUM | false | 按所在文件夹名自动创建/归入相册 |
-A, --album-name <name> | IMMICH_ALBUM_NAME | — | 将所有上传资产加入指定名称的相册 |
--visibility <v> | IMMICH_VISIBILITY | — | 上传资产的可见性:archive/timeline/hidden/locked |
-n, --dry-run | IMMICH_DRY_RUN | false | 只做检查,不执行任何写操作 |
-c, --concurrency <n> | IMMICH_UPLOAD_CONCURRENCY | 见上文说明 | 同时上传的资产数 |
-j, --json-output | IMMICH_JSON_OUTPUT | false | 以 JSON 输出newFiles、duplicates、newAssets |
--delete | IMMICH_DELETE_ASSETS | — | 上传成功后删除本地文件 |
--delete-duplicates | IMMICH_DELETE_DUPLICATES | — | 删除服务端已存在的重复文件 |
--no-progress | IMMICH_PROGRESS_BAR | — | 隐藏进度条 |
--watch | IMMICH_WATCH_CHANGES | false | 监听目录变化并自动上传 |
Quick Start:从零完成一次上传
第一步:认证
# immich login [url] [key] immich login http://192.168.1.216:2283/api HFEJ38DNSDUEG第二步:上传资产
上传单个文件:
immich upload file1.jpg file2.jpg默认不扫描子目录,递归上传整个目录:
immich upload --recursive directory/不确定会发生什么时,先用--dry-run预演,它不会执行任何实际操作:
immich upload --dry-run --recursive directory/第三步:按需组合选项
跳过哈希检查(--skip-hash):默认情况下upload会先对每个文件计算 SHA-1,用来避免重复上传。如果你对文件的唯一性有把握,可以传--skip-hash省掉这一步。注意 Immich 服务端始终会自己做基于哈希的去重,所以这纯粹是性能层面的取舍——带宽充足时跳过客户端哈希可能更快。
immich upload --skip-hash --recursive directory/按文件夹自动建相册(--album):为每个上传资产按其所在文件夹名自动创建相册:
immich upload --album --recursive directory/上传到指定相册(--album-name):把所有资产加入指定名称的相册:
immich upload --album-name "My summer holiday" --recursive directory/用 glob 模式排除文件(--ignore):可以传多个排除模式。glob 的用法可参考 库功能文档:
immich upload --ignore **/Raw/** --recursive directory/immich upload --ignore **/Raw/** **/*.tif --recursive directory/包含隐藏文件(--include-hidden):默认跳过隐藏文件,如需包含:
immich upload --include-hidden --recursive directory/设置可见性(--visibility):把上传资产设为archive、timeline、hidden或locked:
immich upload --visibility archive --recursive directory/JSON 输出(--json-output):输出包含newFiles、duplicates、newAssets三个键的 JSON。由于前面有若干行日志输出,需要去掉输出的前几行才能解析。例如列出将被上传的文件供后续处理:
immich upload --dry-run --json-output . | tail -n +6 | jq .newFiles[]深入源码:一次 upload 到底做了什么
upload的实现集中在 src/commands/asset.ts,主入口upload()(asset.ts#L139-L162)的流程是:认证 → 权限校验 → 扫描文件 → 批量上传。
1. 权限与文件扫描
- 上传要求 API key 具备
AssetUpload权限(asset.ts#L141),否则 CLI 会明确提示缺失的权限名。 scan()会先调用服务端的getSupportedMediaTypes()拿到当前服务器支持的全部图片/视频扩展名,再用 fast-glob 扫描本地目录(src/utils.ts 的crawl)。这解释了为什么--recursive不加时不会进入子目录(模式只加/*而非/**)、--ignore模式会被包装成**/<pattern>匹配全路径、--include-hidden对应 glob 的dot选项。
2. 去重:客户端 SHA-1 + 服务端批量比对
checkForDuplicates()(asset.ts#L179-L310)是--skip-hash所控制的环节:
- 逐文件流式计算 SHA-1(
sha1(),utils.ts#L211-L219),进度条按文件总字节数显示Hashing files/Checking for duplicates两条进度; - 校验项每攒满5000 条就调用一次服务端
checkBulkUpload批量接口,按返回的action分为newFiles(Accept)与duplicates(已存在的资产); - 所有任务通过内部
Queue执行,失败自动重试 3 次,最终逐条报告失败文件。
--skip-hash时则直接跳过本步,把所有文件当作新文件(asset.ts#L180-L183),这正是文档所说"服务端仍会自己哈希去重"的原因。
3. 上传与 XMP sidecar
uploadFile()(asset.ts#L404-L445)通过FormData向POST /assets提交:
fileCreatedAt/fileModifiedAt:取自文件的mtime;fileSize、isFavorite=false、assetData(文件流);visibility:仅在指定--visibility时附加;- sidecarData:
findSidecar()(asset.ts#L447-L457)会自动查找同名 XMP sidecar,支持两种命名:photo.ext.xmp和photo.xmp。存在则一并上传,无需任何额外选项。
上传结果按服务端返回状态统计:新资产计入 success,重复状态(AssetMediaStatus.Duplicate)计入 skipped,结束时打印成功/跳过的数量与字节数,并逐条列出上传失败的文件。
4. 相册、删除本地文件
uploadBatch()(asset.ts#L69-L78)在上传完成后依次执行:
updateAlbums():--album时以资产的父目录名作为相册名(getAlbumName(),asset.ts#L595-L597),先getAllAlbums取已有相册,只创建缺失的,再分批把资产加入对应相册;--album-name则全部归入该固定名称的相册;deleteFiles():--delete删除上传成功的文件,--delete-duplicates删除被判为重复的文件;删除时会顺带unlink对应的 XMP sidecar。注意 dry-run 模式下只打印Would have deleted N local assets。
5. watch 模式:目录监听自动上传
--watch使用 chokidar 监听指定路径(startWatch):
- 只处理服务器支持媒体类型扩展名的文件,
ignore模式同样生效; - 变更事件先汇入
Batcher(utils.ts#L225-L282),每 100 个文件或每 10 秒(UPLOAD_WATCH_BATCH_SIZE/UPLOAD_WATCH_DEBOUNCE_TIME_MS,asset.ts#L27-L28)触发一次批量上传,避免写入中的文件被反复处理(awaitWriteFinish: true); - 初始扫描仍走一次性的
scan()(注释说明 watcher 不处理初始扫描),然后进入长驻监听,Ctrl+C时干净关闭 watcher。
这使得--watch非常适合放在后台长期运行,充当"目录 → Immich"的轻量同步器。
并发模型:Queue 与 fastq
哈希、去重校验、上传三个阶段都使用同一个自研的内存队列封装 src/queue.ts:基于fastq.promise,支持concurrency并行度与retry次数的失败重试(上传链路统一配置为retry: 3)。因此-c, --concurrency不仅影响上传并行度,也决定了本地哈希计算与批量比对请求的并发程度——调大它主要收益在磁盘读与网络并发,调小则对服务器压力更温和。
server-info:查看服务器版本与统计
immich server-info实现见 src/commands/server-info.ts,它并发请求四个接口后输出:
Url:认证后解析出的服务器地址(经.well-known/immich服务发现纠正后);Version:服务器 major.minor.patch 版本;Formats:服务器支持的图片/视频扩展名列表;Statistics:图片数、视频数、资产总数。
该命令要求 API key 同时具备ServerAbout、AssetStatistics、UserRead三个权限,创建 key 时若未勾选对应权限会收到明确的缺失权限提示。
常见问题与使用注意
- URL 必须以
/api结尾:IMMICH_INSTANCE_URL/login的 URL 示例均为https://your-immich-instance/api。连接失败时,logError(utils.ts#L101-L113)会特别提示检查是否有反向代理或 SSO 门户代替了 API 应答。 - 凭证安全:
auth.yml以0o600权限写入,但仍建议用毕logout或删除文件;Docker 场景优先用 env file 而非明文参数。 - dry-run 与 skip-hash 不可同用;
--album与--album-name不可同用,源码中均有显式冲突校验。 - 忽略模式语义:
--ignore按全路径匹配(内部包装为**/<pattern>),写法与 库文档 中扫描设置的排除模式一致,推荐只用于基础的文件夹级排除。 - 版本差异:帮助文本(如
--skip-hash的短选项、--concurrency的默认值标注)可能随版本变化,以你所装版本的immich upload --help实际输出为准;本文引用的行为以当前仓库 packages/cli 源码为准。
小结
Immich CLI 用一条login完成认证、一条upload覆盖绝大多数批量导入需求,全部选项均可用环境变量驱动,天然适配 Docker 与定时任务。理解其源码后可得到几个实用结论:去重是"客户端 SHA-1 + 服务端批量比对"的两段式设计,--skip-hash只省本地计算而服务端去重不受影响;--watch通过 100 文件/10 秒的批处理窗口实现低开销的目录监听;XMP sidecar 会被自动附带上传。配合--dry-run与--json-output,它也能作为脚本化迁移管道的可靠一环。
【免费下载链接】immichHigh performance self-hosted photo and video management solution.项目地址: https://gitcode.com/GitHub_Trending/im/immich
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考