跳转到主要内容

ASAR 存档

在创建 应用程序分发 后,应用程序的源代码通常会被打包到一个 ASAR 归档 中,这是一种专为 Electron 应用程序设计的简单扩展归档格式。通过打包应用程序,我们可以缓解 Windows 上长路径名的问题,加快 require 的速度,并隐藏您的源代码以防止随意检查。

打包后的应用程序运行在一个虚拟文件系统中,大多数 API 都可以正常工作,但有些情况下您可能需要显式地处理 ASAR 归档,因为存在一些注意事项。

使用 ASAR 归档

在 Electron 中,有两组 API:由 Node.js 提供的 Node API 和由 Chromium 提供的 Web API。这两个 API 都支持读取 ASAR 归档中的文件。

Node API

在 Electron 中进行了特殊补丁,Node API(如 fs.readFilerequire)将 ASAR 归档视为虚拟目录,其中的文件视为文件系统中的普通文件。

例如,假设我们有一个名为 example.asar 的归档文件位于 /path/to

$ asar list /path/to/example.asar
/app.js
/file.txt
/dir/module.js
/static/index.html
/static/main.css
/static/jquery.min.js

读取 ASAR 归档中的文件

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 显示 ASAR 归档中的网页

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 获取文件

<script>
let $ = require('./jquery.min.js')
$.get('file:///path/to/example.asar/file.txt', (data) => {
console.log(data)
})
</script>

将 ASAR 归档视为普通文件

在某些情况下,例如验证 ASAR 归档的校验和,我们需要将 ASAR 归档的内容作为文件读取。为此,您可以使用内置的 original-fs 模块,它提供原始的 fs API,不包含 asar 支持

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 归档中的目录。将它们作为某些 API 的 cwd 选项传递也会导致错误。

某些 API 上需要额外的解包

大多数 fs API 可以在不解包的情况下从 ASAR 归档中读取文件或获取文件的信息,但对于某些依赖于将实际文件路径传递给底层系统调用的 API,Electron 会将所需的文件提取到临时文件并传递临时文件的路径给 API 以使其工作。这会为这些 API 增加一些开销。

需要额外解包的 API 包括

  • child_process.execFile
  • child_process.execFileSync
  • fs.open
  • fs.openSync
  • process.dlopen - 用于 require 加载本机模块

fs.stat 的虚假 Stat 信息

fs.stat 及其在 asar 归档中的文件返回的 Stats 对象是通过猜测生成的,因为这些文件不存在于文件系统中。因此,除了获取文件大小和检查文件类型外,您不应该信任 Stats 对象。

执行 ASAR 归档中的二进制文件

存在可以执行二进制文件的 Node API,例如 child_process.execchild_process.spawnchild_process.execFile,但只有 execFile 支持执行 ASAR 归档中的二进制文件。

这是因为 execspawn 接受 command 而不是 file 作为输入,并且 commands 在 shell 下执行。没有可靠的方法可以确定命令是否使用 ASAR 归档中的文件,即使确定了,我们也无法确定在不产生副作用的情况下替换命令中的路径。

将解包的文件添加到 ASAR 归档

如上所述,某些 Node API 在调用时会将文件解包到文件系统。除了性能问题外,各种反病毒扫描程序可能会被此行为触发。

作为一种解决方法,您可以使用 --unpack 选项留下各种未打包的文件。在以下示例中,本机 Node.js 模块的共享库将不会被打包

$ asar pack app app.asar --unpack *.node

运行命令后,您会注意到与 app.asar 文件一起创建了一个名为 app.asar.unpacked 的文件夹。它包含解包的文件,应与 app.asar 归档文件一起发送。