Electron 打包 CloakBrowser:解决客户电脑缺少浏览器二进制的问题
在使用 Electron 和 CloakBrowser 开发浏览器自动化程序时,本地开发环境运行正常,并不代表打包后的程序也能在客户电脑上直接运行。
我最近就遇到了这样一个问题:开发机已经运行过 CloakBrowser,本地存在浏览器缓存,所以程序一直表现正常;但将 Electron 程序交付给客户后,CloakBrowser 无法启动。
排查后发现,Electron 安装包中虽然包含了 cloakbrowser npm 模块,却没有包含 CloakBrowser 实际使用的 Chromium 二进制文件。
本文记录如何把 CloakBrowser 浏览器二进制文件一起打进 Electron 程序,让客户无需单独安装,也不依赖首次启动时联网下载。
问题原因
CloakBrowser 包含两部分:
- npm 中的 JavaScript 调用代码;
- 实际运行的定制 Chromium 浏览器。
安装 cloakbrowser npm 依赖,只会让项目获得调用和管理浏览器的代码。真正的浏览器二进制文件通常会在第一次运行时下载到用户目录:
text
~/.cloakbrowser/
在 Windows 开发机上,对应路径通常是:
text
C:\Users\当前用户名\.cloakbrowser\
例如我的开发机中存在:
text
C:\Users\user\.cloakbrowser\chromium-146.0.7680.177.5\
因此,本地运行时 CloakBrowser 可以直接使用缓存中的 Chromium。但客户电脑没有这个目录,也没有提前下载浏览器,程序就可能无法启动浏览器。
解决思路
整体方案很直接:
- 将当前平台对应的 CloakBrowser Chromium 完整复制到项目中;
- 通过 Electron Forge 的
extraResource将它复制到程序的resources目录; - 程序启动时通过
CLOAKBROWSER_BINARY_PATH指定浏览器路径; - 打包后检查浏览器文件是否存在。
需要注意:CloakBrowser 的 npm 模块和浏览器二进制文件可能采用不同的许可条款。将浏览器二进制文件交付给客户前,应先确认自己已经取得相应的再分发授权。
第一步:复制浏览器文件
不要只复制 chrome.exe。Chromium 运行时还依赖同一目录下的 DLL、资源包、语言文件等内容,必须复制完整目录。
把开发机中的版本目录:
text
C:\Users\user\.cloakbrowser\chromium-146.0.7680.177.5\
完整复制到项目:
text
JobBot\vendor\cloakbrowser\
最终目录结构应当类似:
text
JobBot/
├─ src/
├─ vendor/
│ └─ cloakbrowser/
│ ├─ chrome.exe
│ ├─ chrome.dll
│ ├─ locales/
│ ├─ resources/
│ └─ ...
├─ forge.config.ts
└─ package.json
缓存目录中的以下状态文件不需要复制:
text
.last_update_check
.welcome_shown
它们只是 CloakBrowser 的本地缓存状态,不是 Chromium 运行所需文件。
第二步:配置 Electron Forge
打开 forge.config.ts,在 packagerConfig.extraResource 中加入浏览器目录:
ts
const config: ForgeConfig = {
packagerConfig: {
asar: true,
extraResource: [
'./drizzle',
'./vendor/cloakbrowser',
],
},
}
这里不能把浏览器放进 ASAR 中运行。extraResource 会将目录作为额外资源复制到 Electron 应用的 resources 目录,使 chrome.exe 和其他运行文件保持普通文件形态。
Windows 打包完成后,浏览器路径应当是:
text
resources\cloakbrowser\chrome.exe
第三步:指定 CloakBrowser 浏览器路径
仅仅把文件放进安装包还不够。默认情况下,CloakBrowser 仍然会去 ~/.cloakbrowser 中查找浏览器,因此还要显式设置 CLOAKBROWSER_BINARY_PATH。
在 Electron 主进程入口 src/main.ts 中加入:
ts
import {app, BrowserWindow} from 'electron'
import path from 'node:path'
if (app.isPackaged) {
process.env.CLOAKBROWSER_BINARY_PATH = path.join(
process.resourcesPath,
'cloakbrowser',
'chrome.exe',
)
process.env.CLOAKBROWSER_AUTO_UPDATE = 'false'
}
这里涉及两个 Electron API:
app.isPackaged:判断当前运行的是打包程序还是开发环境;process.resourcesPath:获取打包程序的resources目录。
只在打包环境设置路径,可以让开发环境继续使用 ~/.cloakbrowser 中的浏览器缓存,不影响日常调试。
关闭自动更新也很重要:
ts
process.env.CLOAKBROWSER_AUTO_UPDATE = 'false'
既然程序已经携带经过测试的浏览器版本,就不希望客户端运行时又自动下载其他版本。这样可以保证开发环境、测试环境和客户环境使用相同的 Chromium。
设置环境变量的时机
CLOAKBROWSER_BINARY_PATH 必须在第一次导入或启动 CloakBrowser 之前设置。
例如以下动态导入发生前,环境变量必须已经初始化:
ts
const {launchPersistentContext} = await import('cloakbrowser')
如果项目是在用户点击功能时才动态导入 CloakBrowser,那么把环境变量设置放在 main.ts 顶层即可。
如果项目使用的是静态导入:
ts
import {launchPersistentContext} from 'cloakbrowser'
则建议单独创建一个最先执行的初始化模块,确保设置环境变量的代码不会晚于 CloakBrowser 模块加载。
第四步:重新打包
执行 Electron Forge 打包命令:
bash
npm run make
打包完成后,检查下面的文件是否存在:
text
out\JobBot-win32-x64\resources\cloakbrowser\chrome.exe
同时建议检查整个 cloakbrowser 目录,而不只是 chrome.exe,确认 DLL、资源文件和 locales 都已被复制。
验证方式
不要只在开发机上双击测试,因为开发机的 ~/.cloakbrowser 缓存可能掩盖打包问题。
推荐使用以下任一方式验证:
- 在一台从未安装或运行过 CloakBrowser 的 Windows 虚拟机中测试;
- 临时重命名开发机上的
.cloakbrowser目录,再运行打包产物; - 在程序中临时输出
process.env.CLOAKBROWSER_BINARY_PATH,确认它指向安装包的resources目录。
测试时至少覆盖:
- 程序可以正常启动;
- CloakBrowser 能成功打开;
- 页面可以正常访问;
- 用户数据目录能够保存登录状态;
- 客户端断网时仍能启动已经打包的浏览器。
平台和架构限制
本文复制的是 Windows x64 版本的 CloakBrowser,因此只能用于 Windows x64 安装包。
不同平台的浏览器二进制文件不能混用:
text
Windows x64 -> Windows x64 CloakBrowser
macOS arm64 -> macOS arm64 CloakBrowser
macOS x64 -> macOS x64 CloakBrowser
Linux x64 -> Linux x64 CloakBrowser
如果需要同时发布多个平台,可以按平台保存资源:
text
vendor/
└─ cloakbrowser/
├─ win32-x64/
├─ darwin-arm64/
├─ darwin-x64/
└─ linux-x64/
然后在 Forge 配置中根据当前打包平台选择对应目录,并在运行时拼接相应的可执行文件路径。
安装包体积
CloakBrowser 的 Chromium 二进制文件体积较大,将它放入 Electron 安装包后,安装包体积会明显增加。
这是离线交付的必要成本。它换来的好处是:
- 客户无需安装浏览器;
- 首次运行无需下载约 200 MB 的运行文件;
- 不受客户网络、代理或防火墙影响;
- 浏览器版本固定,交付结果更容易复现。
如果客户设备始终可以联网,也可以不打包浏览器,而是在首次启动时调用 CloakBrowser 的下载能力。但对于需要稳定交付的桌面自动化程序,将经过测试的浏览器版本与程序一起发布通常更可控。