Electron 本地图片在鸿蒙 PC 上白图,我注册了个自定义协议

Electron 本地图片在鸿蒙 PC 上白图,我注册了个自定义协议

上周同事扔给我个需求:在 Electron 应用里加一个图片预览面板,用户点一下就能看项目里的截图。我想了想,这不就一行 <img src="file://..." /> 的事吗,半小时搞定。

结果在鸿蒙 PC 上一跑,傻了。所有图片都是白的。


我先检查一下路径对不对。path.join(__dirname, '../assets/screenshot.png'),拼出来是 /home/user/project/assets/screenshot.png,文件也确实在那儿。换了几种写法:file:/// 三根斜杠、file:// 两根、甚至直接把绝对路径写死,全是白图。

我打开 DevTools,Network 面板里图片请求的状态是 (blocked:NotAllowed)。不是 404,不是 500,是被blocked了。这我就有点懵了------file 协议访问本地文件,这不是天经地义的吗?

查了一个小时鸿蒙文档,终于在一行不起眼的小字上找到了答案:鸿蒙 PC 的安全沙箱策略对 Web 内容的文件访问有限制,file:// 协议默认会被拦截。这和 Windows、macOS 上"本地文件随便读"的行为完全不同。

好,那怎么办?

我第一个想法是关掉 Electron 的 webSecurity。在 BrowserWindow 的配置里加上:

javascript 复制代码
webPreferences: {
  webSecurity: false
}

重启,图片出来了。但这感觉像个定时炸弹------webSecurity 关掉之后,CORS 限制也没了,万一后面接个外部接口,安全隐患太大。而且我在团队群里提了一嘴,斌哥直接回了一句:"你确定要这么干?"

我赶紧把配置改回去了。

第二个想法:不走 file 协议,用 IPC 把图片内容从主进程读到渲染进程,再转成 base64 塞进 img 标签。

javascript 复制代码
// 主进程
ipcMain.handle('read-image', async (_, filePath) => {
  const buffer = await fs.promises.readFile(filePath);
  return `data:image/png;base64,${buffer.toString('base64')}`;
});

// 渲染进程
const base64 = await ipcRenderer.invoke('read-image', imagePath);
img.src = base64;

小图片还行,一张 200KB 的截图,base64 编码后 270KB 左右,肉眼感觉不出延迟。但我试了张 8MB 的设计稿,界面直接卡了将近两秒。base64 的体积膨胀了 33%,而且渲染进程要一次性把字符串塞进 DOM,内存占用也跟着涨。

这条路能走,但算不上好方案。


等等,我漏说一个前提。Electron 其实是支持注册自定义协议的,我之前从来没用过这个功能,一直觉得那是"高级玩法",跟我没关系。

那天晚上我翻 Electron 文档,看到 protocol.registerFileProtocol,突然意识到:既然 file 协议被拦截,那我自己造一个协议不就行了?

原理很简单。我注册一个 app:// 协议,当渲染进程请求 app://assets/screenshot.png 时,主进程拦截这个请求,把路径映射到真实的本地文件,然后返回文件内容。对渲染进程来说,这就是一个普通的 HTTP 请求,鸿蒙的安全策略不会拦截。

代码比我想象的还少:

javascript 复制代码
const { protocol } = require('electron');
const path = require('path');

// 在 app ready 之后注册
app.whenReady().then(() => {
  protocol.registerFileProtocol('app', (request, callback) => {
    const url = new URL(request.url);
    // 把 app:// 映射到应用目录
    const filePath = path.join(__dirname, '..', url.pathname);
    callback(filePath);
  });
});

渲染进程里,img 标签的写法变成:

html 复制代码
<img src="app://assets/screenshot.png" />

就这么简单。重启应用,图片出来了。而且因为走的是 Electron 自己的协议处理器,不经过 Chromium 的 file 安全检查,鸿蒙根本感知不到你在读本地文件。

我顺手测了一下加载速度。用 performance.now() 对比了三种方案加载同一张 3MB 图片的耗时:

  • file:// 在 Windows 上:约 45ms
  • file:// 在鸿蒙上:直接白图(算无限大吧)
  • base64 IPC 方案:约 280ms(包含编码传输和解码)
  • app:// 自定义协议:约 52ms

自定义协议在鸿蒙上的速度和 Windows 上的 file:// 几乎一样,而且比 base64 方案快了五倍多。

更妙的是,这个方案还有额外的好处。以前我的图片路径里混着 path.join(__dirname, ...) 和硬编码字符串,现在全部统一成 app:// 开头的 URL,渲染进程的代码干净了不少。甚至以后如果把资源打包进 asar,也只需要改协议处理器的映射逻辑,渲染层完全不用动。


回头想想,这坑其实不深,就是文档写得太分散了。鸿蒙的安全策略、Electron 的协议注册、Chromium 的 file 访问规则,三个东西各自为政,你得把它们串起来才能想通。

如果你也在 Electron 里读本地文件,而且打算支持鸿蒙 PC,我的建议是直接上自定义协议。别等白图了才开始查。

你遇到过类似的安全策略拦截吗?是怎么绕过去的?


关于我

我叫老三,一个写了十年代码的前端 + 鸿蒙 ArkTS 水手。

目前主业做 Taro 多端项目,业余时间全泡在 AI 自动化和独立开发上------不是因为多热爱加班,而是打心底觉得,程序开发这件事正在被 AI 重构,我不跟上就会被甩下。

这个账号记录的就是我在这条路上的真实经历:踩过的坑、推翻过的方案、以及偶尔值得高兴的小进展。不写教科书,不讲大道理,只分享我自己试过、做过、确认过的东西。

如果你也在写代码,或者也在思考 AI 时代开发者该往哪走------欢迎留言聊聊,一起摸索。

本文遵循 MIT 协议,转载请注明出处。

相关推荐
祭曦念1 小时前
鸿蒙原生 ArkTS 布局探索(一):FlexRowReverse 弹性布局深度解析
华为·harmonyos
Swift社区1 小时前
鸿蒙游戏如何实现稳定 60FPS?
游戏·华为·harmonyos
风华圆舞2 小时前
MethodChannel 在 Flutter 与 ArkTS 之间是怎么工作的
flutter·华为·harmonyos
G_dou_2 小时前
Flutter三方库适配OpenHarmony【prime_checker】质数检测器项目完整实战
flutter·harmonyos
G_dou_2 小时前
Flutter三方库适配OpenHarmony【random_joke】随机笑话应用项目完整实战
flutter·harmonyos
风华圆舞2 小时前
鸿蒙 Flutter 平台通道设计:为什么一项能力一个 channel
flutter·华为·harmonyos
森叶2 小时前
Electron 多进程下的“库引入“全解析:核心模块、Electron API、第三方依赖与 utilityProcess 的依赖处理
运维·javascript·electron
G_dou_2 小时前
Flutter三方库适配OpenHarmony【quote_of_day】每日名言应用项目完整实战
flutter·harmonyos