跳转到主内容

ASAR Archives

在完成应用程序分布后,应用程序的源代码通常会被绑定到ASAR存档中,这是一种为 Electron 应用程序而设计的简易存档格式。通过捆绑到应用程序中,我们可以缓解 Windows 上加载长路径名称的问题,加速加载并隐藏你的源码, 避免粗略的检查。 通过捆绑到应用程序中,我们可以缓解 Windows 上加载长路径名称的问题,加速加载并隐藏你的源码, 避免粗略的检查。

打包后的应用运行在一个虚拟文件系统并且大多数API都可以正常执行, 但有些时候你可能在某种情况下需要在 ASAR 压缩文件内执行.

使用 ASAR 包

Electron 有两套 API:Node API 和 Web API 分别由 Node.js 和 Chromium 提供。 两套 API 都支持都支持从 ASAR 包内读取文件.

Node API

由于 Electron 的特殊补丁程序, Node API 比如 fs.readFilerequire 使用 ASAR 就像是使用虚拟目录一样, 里面的文件也像是在文件系统内一样.

例如,假设我们在 /path/to 文件夹下有个 example.asar 包:

$ 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')

你也可以在 ASAR 存档内使用 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 获取文件:

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

将 ASAR 归档文件看作是一个普通文件

某些情况下比如对 ASAR 归档文件进行校验,我们需要像读取 “文件” 那样读取 ASAR 文件。 为此你可以使用内置的没有asar功能的和原始fs模块一模一样的original-fs模块。

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 的局限性

尽管我们已经尽了最大努力使得 ASAR 文件使用 Node API 时的应用尽可能的趋向于真实的目录结构,但仍有一些底层 Node API 我们无法保证其正常工作。

档案文件是只读的

归档文件中的内容不可更改,所以 Node APIs 里那些会修改文件的方法在使用 ASAR 文件时都无法正常工作.

工作目录不能设置为档案文件里的目录

尽管ASAR档案被视为目录,但实际上文件系统中并不存在真正的目录,因此无法将工作目录设置为ASAR档案中的目录。 将 asar 中的文件夹以 cwd 形式作为参数传入一些 API 中也会报错。

某些 API 需要额外解压档案包

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

以下是一些需要额外解压的 API:

  • child_process.execFile
  • child_process.execFileSync
  • fs.open
  • fs.openSync
  • process.dlopen - 用在 require 原生模块时

fs.stat 的不真实统计信息

asar 档案中的文件取 fs.stat,返回的 Stats 对象不是精确值,因为这些文件不是真实存在于文件系统里。 所以除了文件大小和文件类型以外,你不应该依赖 Stats 对象的值。

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

有一些Node API可以执行二进制文件,例如child_process.execchild_process.spawnchild_process.execFile,但只有execFile支持在ASAR档案内执行二进制文件。

因为 execspawn 允许 command 替代 file 作为输入,而 command 是需要在 shell 下执行的. 目前没有 可靠的方法来判断 command 中是否在操作一个 asar 包中的文件,而且即便可以判断,我们依旧无法保证可以在无任何 副作用的情况下替换 command 中的文件路径。

向 ASAR 档案添加未打包的文件

如上所述,某些 Node API 被调用时会解压文件到文件系统。 除了性能问题外,可能会触犯各种防病毒扫描程序。

你可以把使用--unpack 选项作为将各种文件保持为非压缩状态的一种解决方法。 在下面的示例中,原生Node.js模块的共享库将不会被打包:

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

运行命令后,您将会看到 app.asar.unpacked 文件夹与 app.asar 文件一起被创建了。 没有被打包的文件和 app.asar 会一起存档发布。