第5章 Electron 应用中的 ASAR 归档详解
/app.js
/file.txt
/dir/module.js
/static/index.html
/static/main.css
/static/jquery.min.js
读取归档中的文件: const fs = require('node:fs')
fs.readFileSync('/path/to/example.asar/file.txt')
列出归档根目录下的所有文件: const fs = require('node:fs')
fs.readdirSync('/path/to/example.asar')
使用归档中的模块: require('./path/to/example.asar/dir/module.js')
你也可以用 BrowserWindow 展示归档中的网页: const { BrowserWindow } = require('electron')
const win = new BrowserWindow()
win.loadURL('file:///path/to/example.asar/static/index.html')
### Web API 在网页中,可以通过 file: 协议请求归档内的文件。与 Node API 一样,ASAR 归档会被当作目录处理。 例如,用 $.get 获取文件:
## 将 ASAR 归档当作普通文件处理 某些场景下(比如校验 ASAR 归档的校验和),我们需要把归档当作普通文件来读取其内容。这时可以使用内置的 original-fs 模块,它提供不带 asar 支持的原始 fs API: const originalFs = require('original-fs')
originalFs.readFileSync('/path/to/example.asar')
也可以将 process.noAsar 设为 true,来禁用 fs 模块的 asar 支持: const fs = require('node:fs')
process.noAsar = true
fs.readFileSync('/path/to/example.asar')
Node API 的局限性
尽管我们尽力让 Node API 中的 ASAR 存档行为尽可能接近普通目录,但受限于 Node API 底层特性,仍存在一些限制。
存档只读
存档本身不可修改,因此所有涉及文件修改操作的 Node API 在 ASAR 存档上均无法工作。
无法将工作目录设置为存档内的目录
虽然 ASAR 存档被当作目录处理,但文件系统层并没有真实的目录结构,因此无法将工作目录设置为 ASAR 内部的目录。若通过 cwd 参数将存档内路径传入某些 API,会直接报错。
部分 API 需要额外解包
大多数 fs API 可以直接从 ASAR 存档中读取文件或获取文件信息,无需解包。但依赖真实文件路径来调用底层系统调用的 API 是个例外:此时 Electron 会将所需文件提取到临时文件中,再将该临时文件路径传给 API 以确保其正常运行。这会为这类 API 带来少量额外开销。
需要额外解包的 API 包括:
child_process.execFilechild_process.execFileSyncfs.openfs.openSyncprocess.dlopen(require加载原生模块时用到)
fs.stat 返回的伪 Stat 信息
对 ASAR 存档内的文件调用 fs.stat 及其相关方法时,返回的 Stats 对象其实是估算出来的,因为文件并不真实存在于文件系统中。因此,除了文件大小和文件类型,其他 Stats 字段都不应信任。
执行 ASAR 存档内的二进制文件
有一些 Node API 可以执行二进制文件,如 child_process.exec、child_process.spawn 和 child_process.execFile,但只有 execFile 支持直接执行 ASAR 存档内的二进制文件。
原因在于 exec 和 spawn 接受的是命令字符串而非文件路径,且命令在 shell 下执行。我们无法可靠地判断命令中是否引用了 ASAR 内的文件,即便判断出来,替换命令中的路径也可能产生不可预知的副作用。
为 ASAR 存档添加未打包文件
如上所述,某些 Node API 被调用时会把文件解包到文件系统中。除了性能损耗,这种行为还可能触发各类杀毒软件扫描。作为变通方案,可以使用 --unpack 选项将特定文件保留为未打包状态。以下示例中,原生 Node.js 模块的共享库将不会被打包进 ASAR:
$ asar pack app app.asar --unpack *.node