ASAR 归档
创建 应用分发 后,应用的源代码通常会打包到 ASAR 存档 中,这是一种专为 Electron 应用设计的简单的扩展存档格式。通过打包该应用,我们可以缓解 Windows 上长路径名的问题,加快 require
速度并隐藏源代码以防止粗略检查。
¥After creating an application distribution, the
app's source code are usually bundled into an ASAR archive,
which is a simple extensive archive format designed for Electron apps. By bundling the app
we can mitigate issues around long path names on Windows, speed up require
and conceal your source
code from cursory inspection.
打包的应用在虚拟文件系统中运行,大多数 API 都会正常工作,但在某些情况下,由于一些注意事项,你可能希望明确处理 ASAR 存档。
¥The bundled app runs in a virtual file system and most APIs would just work normally, but for some cases you might want to work on ASAR archives explicitly due to a few caveats.
使用 ASAR 存档
¥Using ASAR Archives
Electron 有两组 API:Node.js 提供的 Node API 和 Chromium 提供的 Web API。两个 API 都支持从 ASAR 存档中读取文件。
¥In Electron there are two sets of APIs: Node APIs provided by Node.js and Web APIs provided by Chromium. Both APIs support reading files from ASAR archives.
Node API
通过 Electron 中的特殊补丁,像 fs.readFile
和 require
这样的 Node API 将 ASAR 存档视为虚拟目录,并将其中的文件视为文件系统中的普通文件。
¥With special patches in Electron, Node APIs like fs.readFile
and require
treat ASAR archives as virtual directories, and the files in it as normal
files in the filesystem.
例如,假设我们在 /path/to
下有一个 example.asar
存档:
¥For example, suppose we have an example.asar
archive under /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 存档中的文件:
¥Read a file in the ASAR archive:
const fs = require('node:fs')
fs.readFileSync('/path/to/example.asar/file.txt')
列出存档根目录下的所有文件:
¥List all files under the root of the archive:
const fs = require('node:fs')
fs.readdirSync('/path/to/example.asar')
使用存档中的模块:
¥Use a module from the archive:
require('./path/to/example.asar/dir/module.js')
你还可以使用 BrowserWindow
显示 ASAR 存档中的网页:
¥You can also display a web page in an ASAR archive with 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 存档也被视为目录。
¥In a web page, files in an archive can be requested with the file:
protocol.
Like the Node API, ASAR archives are treated as directories.
例如,要获取 $.get
的文件:
¥For example, to get a file with $.get
:
<script>
let $ = require('./jquery.min.js')
$.get('file:///path/to/example.asar/file.txt', (data) => {
console.log(data)
})
</script>
将 ASAR 存档视为普通文件
¥Treating an ASAR archive as a Normal File
对于某些情况,例如验证 ASAR 存档的校验和,我们需要将 ASAR 存档的内容作为文件读取。为此,你可以使用内置的 original-fs
模块,该模块提供原始 fs
API,无需 asar
支持:
¥For some cases like verifying the ASAR archive's checksum, we need to read the
content of an ASAR archive as a file. For this purpose you can use the built-in
original-fs
module which provides original fs
APIs without asar
support:
const originalFs = require('original-fs')
originalFs.readFileSync('/path/to/example.asar')
你还可以将 process.noAsar
设置为 true
以禁用 fs
模块中对 asar
的支持:
¥You can also set process.noAsar
to true
to disable the support for asar
in
the fs
module:
const fs = require('node:fs')
process.noAsar = true
fs.readFileSync('/path/to/example.asar')
Node API 的限制
¥Limitations of the Node API
尽管我们努力使 Node API 中的 ASAR 存档尽可能像目录一样工作,但由于 Node API 的底层性质,仍然存在限制。
¥Even though we tried hard to make ASAR archives in the Node API work like directories as much as possible, there are still limitations due to the low-level nature of the Node API.
存档是只读的
¥Archives Are Read-only
存档无法修改,因此所有可以修改文件的 Node API 都不适用于 ASAR 存档。
¥The archives can not be modified so all Node APIs that can modify files will not work with ASAR archives.
工作目录无法设置为存档中的目录
¥Working Directory Can Not Be Set to Directories in Archive
尽管 ASAR 存档被视为目录,但文件系统中没有实际的目录,因此你永远无法将工作目录设置为 ASAR 存档中的目录。将它们作为某些 API 的 cwd
选项传递也会导致错误。
¥Though ASAR archives are treated as directories, there are no actual
directories in the filesystem, so you can never set the working directory to
directories in ASAR archives. Passing them as the cwd
option of some APIs
will also cause errors.
部分 API 的额外解包
¥Extra Unpacking on Some APIs
大多数 fs
API 无需解包即可从 ASAR 存档中读取文件或获取文件信息,但对于某些依赖于将真实文件路径传递给底层系统调用的 API,Electron 会将所需文件提取到临时文件中,并将路径传递给底层系统调用。 API 的临时文件以使它们正常工作。这会为这些 API 增加一些开销。
¥Most fs
APIs can read a file or get a file's information from ASAR archives
without unpacking, but for some APIs that rely on passing the real file path to
underlying system calls, Electron will extract the needed file into a
temporary file and pass the path of the temporary file to the APIs to make them
work. This adds a little overhead for those APIs.
需要额外解压的 API 有:
¥APIs that requires extra unpacking are:
-
child_process.execFile
-
child_process.execFileSync
-
fs.open
-
fs.openSync
-
process.dlopen
- 由require
在原生模块上使用¥
process.dlopen
- Used byrequire
on native modules
fs.stat
的虚假统计信息
¥Fake Stat Information of fs.stat
fs.stat
及其朋友在 asar
存档中的文件上返回的 Stats
对象是通过猜测生成的,因为这些文件在文件系统上不存在。因此,除了获取文件大小和检查文件类型之外,你不应该信任 Stats
对象。
¥The Stats
object returned by fs.stat
and its friends on files in asar
archives is generated by guessing, because those files do not exist on the
filesystem. So you should not trust the Stats
object except for getting file
size and checking file type.
执行 ASAR 存档内的二进制文件
¥Executing Binaries Inside ASAR archive
有一些 Node API 可以执行 child_process.exec
、child_process.spawn
和 child_process.execFile
等二进制文件,但仅支持 execFile
执行 ASAR 存档内的二进制文件。
¥There are Node APIs that can execute binaries like child_process.exec
,
child_process.spawn
and child_process.execFile
, but only execFile
is
supported to execute binaries inside ASAR archive.
这是因为 exec
和 spawn
接受 command
而不是 file
作为输入,并且 command
在 shell 下执行。没有可靠的方法来确定命令是否使用 asar 存档中的文件,即使我们这样做,我们也不能确定是否可以在没有副作用的情况下替换命令中的路径。
¥This is because exec
and spawn
accept command
instead of file
as input,
and command
s are executed under shell. There is no reliable way to determine
whether a command uses a file in asar archive, and even if we do, we can not be
sure whether we can replace the path in command without side effects.
将解压后的文件添加到 ASAR 存档中
¥Adding Unpacked Files to ASAR archives
如上所述,某些 Node API 在调用时会将文件解压到文件系统。除了性能问题之外,此行为还可能会触发各种防病毒扫描程序。
¥As stated above, some Node APIs will unpack the file to the filesystem when called. Apart from the performance issues, various anti-virus scanners might be triggered by this behavior.
作为解决方法,你可以使用 --unpack
选项对各种文件进行解压。在以下示例中,原生 Node.js 模块的共享库将不会被打包:
¥As a workaround, you can leave various files unpacked using the --unpack
option.
In the following example, shared libraries of native Node.js modules will not be
packed:
$ asar pack app app.asar --unpack *.node
运行该命令后,你将注意到与 app.asar
文件一起创建了一个名为 app.asar.unpacked
的文件夹。它包含解压后的文件,应与 app.asar
存档一起提供。
¥After running the command, you will notice that a folder named app.asar.unpacked
was created together with the app.asar
file. It contains the unpacked files
and should be shipped together with the app.asar
archive.