首页
/ Immich 支持的媒体格式全解:从 MIME 类型表到上传校验、库扫描与元数据提取的源码剖析

Immich 支持的媒体格式全解:从 MIME 类型表到上传校验、库扫描与元数据提取的源码剖析

2026-09-04 21:41:48作者:戚魁泉Nursing

Immich 对"哪些文件算照片、哪些文件算视频、哪些文件会被自动索引"的判定,全部集中在一张服务端 MIME 类型表里。本文以官方文档列出的图片/视频支持清单为骨架,结合 mime-types.ts 的完整定义、上传校验与库扫描的实现代码,讲清楚每个格式在 Immich 中的实际处理路径——读完你可以准确回答"某个相机 RAW 文件能否直接上传"、"HEIF 照片的方向信息从哪里来"、"为什么某些格式 Web 端不能直接预览"这类问题。

一、图片格式支持清单

官方文档给出的图片格式支持情况如下(完整清单以源码为准,见 mime-types.ts):

格式 扩展名 支持 备注
AVIF .avif 支持 HEIF 家族,可能含动画
BMP .bmp 支持
GIF .gif 支持 可能含动画
HEIC .heic 支持 HEIF 家族
HEIF .heif 支持 HEIF 家族
JPEG 2000 .jp2 支持
JPEG .jpeg .jpg .jpe .insp 支持 .insp 同样映射为 image/jpeg
JPEG XL .jxl 支持
MPO .mpo 支持 Multi-Picture,多张图片合并的格式,MIME 类型按 image/jpeg 处理
PNG .png 支持 可能含动画(APNG)
PSD .psd 支持 Adobe Photoshop
RAW .raw 支持
RW2 .rw2 支持
SVG .svg 支持
TIFF .tif .tiff 支持
WEBP .webp 支持 可能含动画

源码中的完整图片清单:比文档表格多出的部分

文档表格只列出了"常见"格式,而源码中的 image 映射(由 webSupportedImagewebUnsupportedImageraw 三张表合并而成)实际上还包含:

  • 30 种相机厂商 RAW 扩展名,均映射为厂商专属或 x- 前缀的 MIME 类型,例如 .arw/.sr2/.srf(索尼)、.cr2/.cr3(佳能)、.nef/.nrw(尼康)、.dng(Adobe)、.x3f(适马)、.3fr/.fff(哈苏)等,完整定义见 mime-types.ts#L4-L35。这意味着主流相机厂商的 RAW 文件都可以直接作为资产上传,而不是仅支持文档表格中列出的 .raw.rw2 两种;
  • .hif(小米手机的 HEIF 变体),同样被列入 HEIF 家族;
  • .vob 对应的视频扩展名(见下文视频章节,文档表格中未列出)。

一个值得注意的细节:文档标注为"RAW"的 .raw 在源码中对应 image/rawimage/x-panasonic-raw 两个 MIME 候选(mime-types.ts#L28),即它是松下(Panasonic)的 RAW 格式,而索尼 .arw、尼康 .nef 等各自独立登记。

Web 浏览器能否直接预览:webSupported 与 webUnsupported 的划分

源码把图片显式分成两组(mime-types.ts#L37-L70):

  • webSupportedImage.avif .bmp .gif .jpeg/.jpg .png .webp——浏览器原生可渲染,Web 端可直接显示原图;
  • webUnsupportedImage:所有 RAW 扩展名、.heic/.heif/.hif.jp2 .jxl .svg .tif/.tiff.mpo.insp 等——浏览器无法直接解码。

这解释了一个实际使用中的现象:上传 HEIC/HEIF 或 RAW 照片后,Web 界面展示的是服务端生成的 JPEG/WebP 预览图,而非直接加载原文件。isWebSupportedImage()isHeifImage() 这两个导出方法就是围绕这两个集合构建的(mime-types.ts#L157-L158)。

二、视频格式支持清单

文档列出的视频格式如下:

格式 扩展名 支持
3GPP .3gp .3gpp 支持
AVI .avi 支持
FLV .flv 支持
M4V .m4v 支持
MATROSKA .mkv 支持
MP2T .mts .m2ts .m2t .ts 支持
MP4 .mp4 .insv 支持(.insv 映射为 video/mp4
MPEG .mpg .mpe .mpeg 支持
MXF .mxf 支持
QUICKTIME .mov 支持
WEBM .webm 支持
WMV .wmv 支持

源码中 video 映射与文档一致(mime-types.ts#L106-L127),并额外登记了 .vobvideo/mpeg,DVD 光盘常见的 MPEG 程序流)。两个与文档不同的实现细节值得留意:

  1. .mxf 的 MIME 类型是 application/mxf 而非 video/ 前缀,因此 assetType() 在判定资产类型时必须把它显式并入视频分支:
// server/src/utils/mime-types.ts
if (contentType === 'application/mxf' || contentType.startsWith('video/')) {
  return AssetType.Video;
}
  1. .avi 登记了 4 个历史 MIME 候选video/avivideo/msvideovideo/vnd.avivideo/x-msvideo),.mp4/.insv 均归入 video/mp4lookup() 始终取数组第一个候选作为规范 MIME 类型,其余候选仅用于 toExtension() 的反向查找。

XMP 侧车文件:文档之外的重要第三类

mime-types.ts 中还定义了 sidecar 类别(mime-types.ts#L129-L131):

const sidecar: Record<string, string[]> = {
  '.xmp': ['application/xml', 'text/xml'],
};

.xmp 是 RAW 照片的 EXIF 侧车文件,与图片本身同名同目录。Immich 把侧车文件作为独立的上传类别处理(详见 XMP Sidecars 文档),并在元数据提取时优先采用侧车中的日期信息。

三、格式判定如何贯穿整个服务端

mimeTypes 模块导出的判定函数是格式体系的"消费者",理解它们就能理解 Immich 对文件类型的全部处理逻辑(mime-types.ts#L147-L181):

方法 作用
isAsset(filename) 是否为受支持资产(图片或视频)
isImage / isVideo / isRaw 单类别判定
isWebSupportedImage 浏览器能否直接渲染
isHeifImage 是否属于 .avif/.heic/.heif/.hif 家族
isPossiblyAnimatedImage 是否可能含动画帧(.avif .gif .heic .heif .jxl .png .webp
isProfile(filename) 是否可作为头像(.avif .dng .heic .heif .jpeg .jpg .png .svg .webp
isSidecar(filename) 是否 .xmp 侧车文件
canBeTransparent(filename) 格式是否具备透明通道能力(.avif .bmp .gif .heic .heif .hif .jxl .png .svg .tif .tiff .webp
lookup(filename) 文件名 → MIME 类型,未识别时回退为 application/octet-stream
toExtension(mimeType) MIME 类型 → 扩展名,其中 image/jpeg 固定返回 .jpg(见 extensionOverrides
assetType(filename) 归一为 Image / Video / Other 三类资产
getSupportedFileExtensions() 返回全部受支持扩展名列表,供库扫描与存储过滤使用

上传校验:不支持的格式在入口即被拒绝

上传接口按字段名区分三类上传内容,并在 canUploadFile() 中做格式门禁(asset-media.service.ts#L59-L89):

switch (fieldName) {
  case UploadFieldName.ASSET_DATA: {
    if (mimeTypes.isAsset(filename)) {
      return true;
    }
    break;
  }
  case UploadFieldName.SIDECAR_DATA: {
    if (mimeTypes.isSidecar(filename)) {
      return true;
    }
    break;
  }
  case UploadFieldName.PROFILE_DATA: {
    if (mimeTypes.isProfile(filename)) {
      return true;
    }
    break;
  }
}

this.logger.error(`Unsupported file type ${filename}`);
throw new BadRequestException(`Unsupported file type ${filename}`);

也就是说:资产上传必须命中图片或视频表,.xmp 只能通过侧车通道上传,头像则受更严格的 profile 白名单限制——例如你不能用 .tiff.mpo 设置头像。判定基于文件扩展名(内部通过 getFilenameExtension() 取扩展名并转小写后查表),而非嗅探文件头。

库自动索引:扩展名列表驱动文件监控

当启用库(Library)的文件监控时,Immich 用全部受支持扩展名动态构造 picomatch 匹配器(library.service.ts#L103-L106):

const matcher = picomatch(`**/*{${mimeTypes.getSupportedFileExtensions().join(',')}}`, {
  nocase: true,
  ignore: library.exclusionPatterns,
});

匹配成功的文件变更会入队 LibrarySyncFiles 任务进行索引,未匹配的(如 .pdf.txt)则直接忽略;同时库级排除模式 exclusionPatterns 仍优先生效。同理,存储层查询也基于同一份扩展名列表构造过滤条件(storage.repository.ts#L286)。这保证了一个关键的一致性:上传接口、库扫描、存储查询看到的"受支持格式"永远是同一张表,不会各走各的。

四、特殊格式的元数据提取逻辑

格式清单定义了"能不能进",而进入之后不同格式还需要差异化处理。metadata.service.ts 中有两处与格式直接相关的分支:

HEIF 家族的方向(Orientation)修正

// don't use Exif Orientation for HEIF based images, it's usually missing or invalid.
// prefer irot (ExifTool QuickTime:Rotation) mapped to ExifOrientation.
if (mimeTypes.isHeifImage(asset.originalPath)) {
  const orientation = this.getHeifOrientation(mediaTags);
  ...
}

metadata.service.ts#L604-L613)HEIC/HEIF/AVIF/HIF 照片的 Exif Orientation 标签通常缺失或不可靠,Immich 改从 QuickTime 的 irot 标签推导方向,推导不出则直接删除该标签,避免按错误方向渲染手机拍的照片。

动画图像与时长(Duration)标签

// prefer duration from video tags
// don't save duration if asset is definitely not an animated image (see e.g. CR3 with Duration: 1s)
if (videoResult || !mimeTypes.isPossiblyAnimatedImage(asset.originalPath)) {
  delete mediaTags.Duration;
}

metadata.service.ts#L595-L599)视频资产的时长一律以容器探测结果为准;对"可能是动画"的图片(AVIF/GIF/HEIC 等)保留 Exif 侧的时长;而对 .tiff.cr3 这类静态格式则丢弃 Duration 标签——注释中明确提到佳能 CR3 会带一个无意义的 Duration: 1s

RAW 嵌入缩略图的提取

RAW 文件内部通常嵌入了一张贴图用 JPEG。服务端通过管理端配置项 image.extractEmbedded("Extract embedded",见 config.dto.ts#L348)控制是否对 mimeTypes.isRaw(...) 为真的资产执行嵌入 JPEG 提取(media.service.ts#L259media.service.ts#L412),用于在无法解码 RAW 时仍有可显示的预览。

五、测试如何锁定这张格式表

mime-types.spec.ts 对上述实现做了系统性约束:

  • 排序的 MIME 与扩展名映射表逐条断言 lookup 结果(如 { mimetype: 'image/cr3', extension: '.cr3' }{ mimetype: 'video/mp2t', extension: '.m2t' } 等约百条用例),新增或改动格式必须同步更新测试;
  • 断言 image/video/sidecar/profile 各表的键与值全部小写,且 videosidecar 表的键必须保持排序(mime-types.spec.ts#L214-L217);
  • 验证 image/ 前缀纯度(image 表不允许混入非图片 MIME)、canBeTransparent 的正反例集合、isPossiblyAnimatedImage 对动画/静态/视频三类输入的判定,以及 toExtension('image/jpeg') 固定返回 .jpg 的覆盖规则。

六、小结

Immich 的格式支持可以用三层来概括:

  1. 单一事实来源mime-types.tsraw(30 种厂商 RAW)、webSupportedImage/webUnsupportedImage(浏览器可渲染与否)、videosidecar 四张表合并出全部判定依据;
  2. 统一消费:上传门禁(asset-media.service.ts)、库文件监控与索引(library.service.ts)、存储过滤(storage.repository.ts)全部复用同一组 mimeTypes.* 方法;
  3. 差异化处理:HEIF 家族的方向修正、动画图像的时长保留、RAW 嵌入 JPEG 提取,均在元数据/媒体服务中按格式分支处理,并有 mime-types.spec.ts 锁定行为。

如果你需要确认某个扩展名是否受支持,最快路径就是直接查 mime-types.ts 中的表——文档表格是其常见子集,源码表才是完整清单。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384