一、这篇文章想讲什么欢迎加入鸿蒙PC开发者社区共同打造开发者工具生态鸿蒙PC开发者社区 https://harmonypc.csdn.net/这篇文章不是单纯展示“我把一个 Electron 项目跑在鸿蒙上了”而是想把一个更真实的问题讲清楚一个原本强依赖桌面环境能力的 Electron 应用迁移到 OpenHarmony 之后怎么一步一步从“可以打开界面”走到“真的可以处理用户文件”。这次选择的项目是ExifCleaner。如果只看表面它只是一个界面很简洁的小工具拖进去一批图片或者文档清掉其中的 metadata然后导出干净文件。但真开始改的时候很快就会发现它的难点根本不在界面而在底层能力上。桌面版之所以好用不是因为 Electron 写得多花哨而是因为它背后站着一个非常强的工具ExifTool。问题也正出在这里。ExifTool本质上是 Perl 工具链在桌面系统里问题不大但到了鸿蒙这一侧事情就完全变了。应用虽然能被拉起来但真正做 metadata 处理时很快就会出现进程起不来、资源路径不对、运行时依赖缺失、格式处理链断掉等一串问题。所以这次适配的核心不是“如何让 Electron 在鸿蒙显示一个窗口”而是先把应用的主流程跑通。再把原来依赖ExifTool的那部分能力一点点接管回来。最后把高频格式覆盖到“日常可用”。这篇文章就按这个过程展开。该项目适配鸿蒙版本已经开源到https://atomgit.com/lqjmac/exifcleaner-ohos二、项目起点窗口打开了但功能几乎不可用刚开始接这个项目的时候最容易产生一种错觉既然界面已经能起来那是不是离“适配完成”就不远了实际完全不是。应用在鸿蒙上首次跑起来以后最早看到的问题不是 UI 样式错乱而是一串比较扎眼的运行日志比如ExifTool is not runningspawn /.resources/nix/bin/exiftool ENOENTstrings.json读取失败icon.png路径错误文件拖进来之后能看到列表但 metadata 读取不出来换句话说应用表面上已经“活着”但核心功能其实是断的。ExifCleaner的主链路很简单用户导入文件读取原始 metadata调用清理逻辑再读取一次清理后的 metadata在界面里展示 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/...这就意味着原来那些“默认以为资源就在某个相对目录”的写法会在鸿蒙上大量失效。这个阶段做的事情主要有两类统一资源路径解析逻辑调整鸿蒙打包脚本把应用运行真正需要的资源放进正确目录这里我额外做了一个处理在鸿蒙包里单独引入app-resources目录用它来放图标、字符串文件和后续需要的资源。这样路径逻辑会比沿用桌面的隐藏目录更稳定一些。这一步处理完以后像strings.json、icon.png、检查图标这些资源读取问题就明显少了很多至少不会动不动出现“文件明明打进包里了但运行时就是找不到”的情况。五、先解燃眉之急把最常用图片格式做成可用版在适配这类工具时我一直比较反对一上来就追求“支持一切格式”。更实用的做法是先问一个问题用户平时最常拿来清 metadata 的文件到底是什么答案其实很明确首先就是图片尤其是JPG / JPEGPNG所以前期工作的重点非常集中就是先把这两条线做通。JPEG 这条线做了什么对 JPEG我主要补了这几类能力识别APP1 Exif识别XMP识别ICC Profile识别 IPTC / Photoshop IRB清理 metadata 时支持全删保留方向信息保留 ICC 色彩配置尤其是“保留方向”这一点如果没有处理好用户会觉得功能坏了。因为照片虽然 metadata 被清掉了但图片方向突然不对了体验上就会像是清理逻辑把文件搞坏了一样。PNG 这条线做了什么PNG 的情况和 JPEG 不太一样重点在于各种 chunkeXIftEXtzTXtiTXtiCCPtIME这部分做完以后至少在鸿蒙上已经能比较稳定地处理截图、网络图片、带文本块的 PNG 文件了。六、从“图片可用”走向“主流格式可用”JPEG 和 PNG 打通之后后面的工作就不再是“从零到一”而更像是不断把同类文件体系往外扩。1. WebPWebP 相对清晰一些主要处理EXIFXMPICCP同时把 metadata 清理和保留色彩配置这部分一起补上。2. HEIC / HEIF / AVIF这几类格式是中期适配里非常关键的一段因为手机拍照场景里它们的占比很高。它们本质上都属于 ISO BMFF 容器家族所以这一阶段的重点变成了解析Exifbox解析XMPbox递归处理容器结构清理时重建 box 结构保留 orientation最开始这里踩过一个比较典型的坑表面上已经“接了 HEIC 支持”但一跑测试才发现 TIFF 偏移找错了结果Make/Model根本读不出来。后来修掉这个偏移问题之后HEIC/HEIF 才算真正进入可用状态。AVIF 这块后面就顺势复用了 HEIF 这套能力所以扩展起来比较快。3. TIFF / DNGTIFF 是另一个关键节点因为它不只是一个普通图片格式还直接关系到很多 RAW 体系的基础。这部分除了基础 EXIF 读取我还补了XMPICC_ProfilepreserveOrientationpreserveColorProfile后面做DNG的时候基本就是复用 TIFF 这条线。4. GIFGIF 这块没有前面那些格式复杂但我也不想让它在鸿蒙上直接掉成“不支持”。所以这部分补的是comment 扩展块application 扩展块XMP 相关扩展对应 metadata 的清理七、只做图片还不够视频和 PDF 也得补如果只做图片这个工具依然不能算真正好用。因为现实使用里视频和 PDF 也经常会出现在“需要去 metadata”的列表里。MP4 / MOV / M4V这一块我先做的是一个基础但实用的版本重点是 QuickTime 容器里比较常见的几类 metadatamoovudtametailstXMP_对应地鸿蒙侧能做的事情包括读取常见 QuickTime metadata读取 XMP清理常见容器 metadata 块它还远没有达到 ExifTool 对视频容器那种很深的覆盖但至少已经不是“视频文件导入后什么都干不了”。PDFPDF 这块我采取的是比较克制的做法。没有直接上一个完整 PDF 对象重写器而是先支持读取Info字典中的常见字段AuthorCreatorTitle识别 PDF 里的 XMP清理 ExifTool 风格的 PDF 增量更新 metadata 段这么做的原因很简单当前项目里最常见、也最现实的 PDF 场景就是这类结构先把它做稳比一开始追求“大而全”更靠谱。八、RAW 的第一步先从 DNG 和 CR3 切进去做完高频图片、视频和 PDF 之后后面最有代表性的工作就是 RAW。这一块如果想一步做到和 ExifTool 同等级几乎不现实。因为 RAW 家族不是一个格式而是一整个世界每家厂商都有自己的私有结构和细节。所以我的策略还是一样先从最容易接进去、收益又足够高的点开始。DNGDNG本质上还是 TIFF 体系所以它是最适合作为 RAW 第一站的。有了前面 TIFF 的基础以后DNG 这一步比较顺读取 metadata清理 metadata复用 TIFF 里的 orientation / ICC / XMP 相关逻辑CR3CR3则是另一种情况它更接近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 做得更完整至少都已经站在一条可持续演进的路线上了。如果把这次工作浓缩成一句话我会这样概括鸿蒙适配最难的从来不是把窗口拉起来而是把桌面环境里那些理所当然的底层能力重新在新平台上接回来。