一、这篇文章想讲什么

欢迎加入鸿蒙PC开发者社区,共同打造开发者工具生态:鸿蒙PC开发者社区 :https://harmonypc.csdn.net/

这篇文章不是单纯展示“我把一个 Electron 项目跑在鸿蒙上了”,而是想把一个更真实的问题讲清楚:

一个原本强依赖桌面环境能力的 Electron 应用,迁移到 OpenHarmony 之后,怎么一步一步从“可以打开界面”,走到“真的可以处理用户文件”。

这次选择的项目是 ExifCleaner

如果只看表面,它只是一个界面很简洁的小工具:拖进去一批图片或者文档,清掉其中的 metadata,然后导出干净文件。

但真开始改的时候,很快就会发现它的难点根本不在界面,而在底层能力上。桌面版之所以好用,不是因为 Electron 写得多花哨,而是因为它背后站着一个非常强的工具:ExifTool

问题也正出在这里。

ExifTool 本质上是 Perl 工具链,在桌面系统里问题不大,但到了鸿蒙这一侧,事情就完全变了。应用虽然能被拉起来,但真正做 metadata 处理时,很快就会出现进程起不来、资源路径不对、运行时依赖缺失、格式处理链断掉等一串问题。

所以这次适配的核心,不是“如何让 Electron 在鸿蒙显示一个窗口”,而是:

  1. 先把应用的主流程跑通。
  2. 再把原来依赖 ExifTool 的那部分能力,一点点接管回来。
  3. 最后把高频格式覆盖到“日常可用”。

这篇文章就按这个过程展开。

该项目适配鸿蒙版本已经开源到:https://atomgit.com/lqjmac/exifcleaner-ohos


二、项目起点:窗口打开了,但功能几乎不可用

刚开始接这个项目的时候,最容易产生一种错觉:既然界面已经能起来,那是不是离“适配完成”就不远了?

实际完全不是。

应用在鸿蒙上首次跑起来以后,最早看到的问题不是 UI 样式错乱,而是一串比较扎眼的运行日志,比如:

  • ExifTool is not running
  • spawn /.resources/nix/bin/exiftool ENOENT
  • strings.json 读取失败
  • icon.png 路径错误
  • 文件拖进来之后能看到列表,但 metadata 读取不出来

换句话说,应用表面上已经“活着”,但核心功能其实是断的。

ExifCleaner 的主链路很简单:

  1. 用户导入文件
  2. 读取原始 metadata
  3. 调用清理逻辑
  4. 再读取一次清理后的 metadata
  5. 在界面里展示 before / after 差异

而在鸿蒙上,断点恰好在第 2 步和第 3 步。因为这两步原来都建立在 ExifTool 可正常启动的前提上。

也就是说,如果不把 ExifTool 依赖替换掉,应用虽然能打开,但实际上没有办法完成自己的核心任务。

在这里插入图片描述


三、先不要急着谈“全量支持”,第一步是先把处理链接管下来

一开始我也想过两种路线:

路线一:继续想办法把桌面版 ExifTool 搬过去

这条路听起来最省事,因为理论上只要能把原来的 Perl 和资源包完整跑起来,桌面版大部分能力都能继承。

但问题是,这条路在鸿蒙上并不稳定,而且从长期看也不划算。

原因很直接:

  • 运行时依赖重
  • 资源路径和启动方式和桌面完全不同
  • 即使勉强拉起来,后面维护成本也会很高
  • 一旦要做打包和分发,问题只会更多

路线二:在鸿蒙侧重新接管 metadata 处理能力

最终我选择了这条路。

具体做法不是“一次性重写 ExifTool”,那也不现实,而是引入一个新的适配层:

HarmonyMetadataAdapter

这个适配层做的事情很明确:

  • 桌面环境下,仍然走原来的 ExifToolAdapter
  • 鸿蒙环境下,切到 HarmonyMetadataAdapter

这样一来,应用上层的调用方式不用大改,改动点会被尽量控制在基础设施层。

这一步非常重要,因为它意味着项目开始从“强依赖桌面工具”转向“鸿蒙侧有自己的一套处理入口”。

说白一点,这一步不是为了功能一下子变全,而是为了把主动权拿回来。

在这里插入图片描述


四、第二个坑:资源路径在鸿蒙上不是桌面那一套

把 metadata 适配入口接出来之后,问题并没有结束。

很快又遇到另一个非常现实的问题:资源路径不对。

桌面 Electron 项目里,很多资源读取习惯上会假设路径长这样:

  • /.resources/icon.png
  • /.resources/strings.json
  • /.resources/nix/bin/exiftool

但鸿蒙打包出来以后,资源落点并不是这套路径结构。

实际调试时能看到,应用资源更像是被放到一层打包目录下面,比如:

/data/storage/el1/bundle/.../resources/resfile/resources/app/...

这就意味着,原来那些“默认以为资源就在某个相对目录”的写法,会在鸿蒙上大量失效。

这个阶段做的事情主要有两类:

  1. 统一资源路径解析逻辑
  2. 调整鸿蒙打包脚本,把应用运行真正需要的资源放进正确目录

这里我额外做了一个处理:在鸿蒙包里单独引入 app-resources 目录,用它来放图标、字符串文件和后续需要的资源。这样路径逻辑会比沿用桌面的隐藏目录更稳定一些。

这一步处理完以后,像 strings.jsonicon.png、检查图标这些资源读取问题就明显少了很多,至少不会动不动出现“文件明明打进包里了,但运行时就是找不到”的情况。

在这里插入图片描述


五、先解燃眉之急:把最常用图片格式做成可用版

在适配这类工具时,我一直比较反对一上来就追求“支持一切格式”。更实用的做法是先问一个问题:

用户平时最常拿来清 metadata 的文件,到底是什么?

答案其实很明确,首先就是图片,尤其是:

  • JPG / JPEG
  • PNG

所以前期工作的重点非常集中,就是先把这两条线做通。

JPEG 这条线做了什么

对 JPEG,我主要补了这几类能力:

  • 识别 APP1 Exif
  • 识别 XMP
  • 识别 ICC Profile
  • 识别 IPTC / Photoshop IRB
  • 清理 metadata 时支持:
    • 全删
    • 保留方向信息
    • 保留 ICC 色彩配置

尤其是“保留方向”这一点,如果没有处理好,用户会觉得功能坏了。因为照片虽然 metadata 被清掉了,但图片方向突然不对了,体验上就会像是清理逻辑把文件搞坏了一样。

PNG 这条线做了什么

PNG 的情况和 JPEG 不太一样,重点在于各种 chunk:

  • eXIf
  • tEXt
  • zTXt
  • iTXt
  • iCCP
  • tIME

这部分做完以后,至少在鸿蒙上已经能比较稳定地处理截图、网络图片、带文本块的 PNG 文件了。

在这里插入图片描述


六、从“图片可用”走向“主流格式可用”

JPEG 和 PNG 打通之后,后面的工作就不再是“从零到一”,而更像是不断把同类文件体系往外扩。

1. WebP

WebP 相对清晰一些,主要处理:

  • EXIF
  • XMP
  • ICCP

同时把 metadata 清理和保留色彩配置这部分一起补上。

2. HEIC / HEIF / AVIF

这几类格式是中期适配里非常关键的一段,因为手机拍照场景里它们的占比很高。

它们本质上都属于 ISO BMFF 容器家族,所以这一阶段的重点变成了:

  • 解析 Exif box
  • 解析 XMP box
  • 递归处理容器结构
  • 清理时重建 box 结构
  • 保留 orientation

最开始这里踩过一个比较典型的坑:表面上已经“接了 HEIC 支持”,但一跑测试才发现 TIFF 偏移找错了,结果 Make/Model 根本读不出来。后来修掉这个偏移问题之后,HEIC/HEIF 才算真正进入可用状态。

AVIF 这块后面就顺势复用了 HEIF 这套能力,所以扩展起来比较快。

3. TIFF / DNG

TIFF 是另一个关键节点,因为它不只是一个普通图片格式,还直接关系到很多 RAW 体系的基础。

这部分除了基础 EXIF 读取,我还补了:

  • XMP
  • ICC_Profile
  • preserveOrientation
  • preserveColorProfile

后面做 DNG 的时候,基本就是复用 TIFF 这条线。

4. GIF

GIF 这块没有前面那些格式复杂,但我也不想让它在鸿蒙上直接掉成“不支持”。所以这部分补的是:

  • comment 扩展块
  • application 扩展块
  • XMP 相关扩展
  • 对应 metadata 的清理

在这里插入图片描述


七、只做图片还不够,视频和 PDF 也得补

如果只做图片,这个工具依然不能算真正好用。因为现实使用里,视频和 PDF 也经常会出现在“需要去 metadata”的列表里。

MP4 / MOV / M4V

这一块我先做的是一个基础但实用的版本,重点是 QuickTime 容器里比较常见的几类 metadata:

  • moov
  • udta
  • meta
  • ilst
  • XMP_

对应地,鸿蒙侧能做的事情包括:

  • 读取常见 QuickTime metadata
  • 读取 XMP
  • 清理常见容器 metadata 块

它还远没有达到 ExifTool 对视频容器那种很深的覆盖,但至少已经不是“视频文件导入后什么都干不了”。

PDF

PDF 这块我采取的是比较克制的做法。

没有直接上一个完整 PDF 对象重写器,而是先支持:

  • 读取 Info 字典中的常见字段
    • Author
    • Creator
    • Title
  • 识别 PDF 里的 XMP
  • 清理 ExifTool 风格的 PDF 增量更新 metadata 段

这么做的原因很简单:当前项目里最常见、也最现实的 PDF 场景就是这类结构,先把它做稳,比一开始追求“大而全”更靠谱。

在这里插入图片描述


八、RAW 的第一步:先从 DNG 和 CR3 切进去

做完高频图片、视频和 PDF 之后,后面最有代表性的工作就是 RAW。

这一块如果想一步做到和 ExifTool 同等级,几乎不现实。因为 RAW 家族不是一个格式,而是一整个世界,每家厂商都有自己的私有结构和细节。

所以我的策略还是一样:先从最容易接进去、收益又足够高的点开始。

DNG

DNG 本质上还是 TIFF 体系,所以它是最适合作为 RAW 第一站的。

有了前面 TIFF 的基础以后,DNG 这一步比较顺:

  • 读取 metadata
  • 清理 metadata
  • 复用 TIFF 里的 orientation / ICC / XMP 相关逻辑

CR3

CR3 则是另一种情况,它更接近 HEIF / AVIF 那条 ISO BMFF 容器路线。

所以这里我先做了一个基础版:

  • 读取常见 EXIF metadata box
  • 清理常见 metadata box

这还不能说已经完整支持 Canon RAW,但它至少把 CR3 从“完全不支持”推进到了“有基础处理能力”。

这一步完成以后,鸿蒙版的格式覆盖就开始有点体系化的味道了,而不是只靠几种常见图片撑场面。


九、这次适配里,我最在意的不是“支持了多少格式”,而是几件更底层的事

如果只看提交记录,这次工作像是在不停给 HarmonyMetadataAdapter 加格式支持。

但真正决定这次适配有没有价值的,其实是下面几件事。

1. 不再把鸿蒙适配建立在桌面运行时侥幸可用的前提上

这是最根本的一点。

如果底层还是强依赖桌面版 ExifTool,那所有适配都只是在修表层问题。看起来修好了,实际随时会被运行环境一刀切回原形。

2. 改动尽量集中在基础设施层,而不是把业务层打散

这次大部分能力接管,都尽量压在:

  • HarmonyMetadataAdapter
  • 资源路径解析
  • 鸿蒙打包脚本

这样做的好处是,上层界面、主流程、IPC 调用不用被大面积重写,后续维护会轻很多。

3. 每补一种能力,都尽量带测试

这一点非常重要。

因为 metadata 处理不是一个“看起来能跑就算好了”的领域。很多时候代码能过编译,甚至文件也能导出,但读取出来的字段不对、偏移错了、清理后文件结构变了,这些问题如果没有测试,很容易越改越乱。

这次后面之所以能越改越快,和前面持续补 vitest 回归测试关系很大。

在这里插入图片描述


十、现在这个版本到底到了什么程度

做到这里,我觉得可以比较坦率地给一个判断。

已经做到的

当前鸿蒙版已经具备了下面这些基础:

  • 应用可启动
  • 主界面可正常显示
  • 文件导入流程可跑通
  • 文件大小等基础信息可展示
  • 多类主流格式已经有真实 metadata 读取能力
  • 多类主流格式已经有真实 metadata 清理能力

目前已经接入并经过回归测试验证的主要类型包括:

  • 图片:JPEG / PNG / GIF / WebP / HEIC / HEIF / AVIF / TIFF / DNG / CR3
  • 视频容器:MP4 / MOV / M4V
  • 文档:PDF

十一、这次改造给我的一个很直接的感受

以前做跨平台适配时,我常常会先盯着“界面能不能起来”。这当然也重要,但这次把 ExifCleaner 改到鸿蒙上以后,我反而更确定了一件事:

很多 Electron 项目的真正门槛,不在 UI,而在桌面默认依赖的那堆底层能力。

桌面环境太“宽容”了,很多项目在 macOS、Windows、Linux 上跑久了,会自然默认:

  • 路径就该这么写
  • 进程就该这么起
  • 资源就该在这个位置
  • 外部工具就该随时能调用

一旦到了鸿蒙,这些默认前提会被一项一项拆掉。

从这个角度看,这次适配其实不是简单的“把项目搬过去”,而是在倒逼我重新理解这个项目到底靠什么活着。

ExifCleaner 表面上是一个 Electron 小工具,但它真正的核心能力并不在 React 组件里,而在 metadata 处理引擎里。把这个认清楚以后,后面的路就反而清楚了:

  • 先接管能力入口
  • 再覆盖高频格式
  • 再往复杂格式递进

这条路线虽然不炫,但很稳。


十二、如果你也在做类似项目的鸿蒙适配,我会建议你优先做这几件事

最后给几个很实际的建议,都是这次改造里踩过坑之后留下来的。

1. 先搞清楚项目真正依赖的“桌面能力”是什么

别被 UI 迷惑。很多项目真正难适配的地方,都在进程、文件、资源、外部二进制、系统 API 这些地方。

2. 优先做一层平台适配,而不是全局到处写 if else

像这次的 HarmonyMetadataAdapter,虽然前期投入不小,但一旦立住,后续就会越来越顺。

3. 不要一开始就追求“全格式支持”

先把高频格式做稳,比一口气列一长串支持表有意义得多。

4. 资源路径问题要尽早统一

这个坑非常常见,而且会反复出现。越晚处理,后面越难查。

5. 测试一定要跟上

尤其是文件格式处理这种活,一旦没有测试,后面改一个格式很可能把另一个格式带坏。


十三、结语

回头看这次适配过程,我觉得它最有价值的地方,不是“最终支持了多少种格式”,而是把一件事情做实了:

鸿蒙版 ExifCleaner 不再只是一个界面能打开的壳,而是开始具备真正处理文件的能力。

这一步做出来之后,后面无论是继续补 RAW、补更深的视频 metadata,还是把 PDF 做得更完整,至少都已经站在一条可持续演进的路线上了。

如果把这次工作浓缩成一句话,我会这样概括:

鸿蒙适配最难的,从来不是把窗口拉起来,而是把桌面环境里那些理所当然的底层能力,重新在新平台上接回来。


Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐