Skip to main content

ASAR 归档

在创建应用分发之后,应用的源代码通常会被打包成ASAR归档,这是一种为Electron应用设计的简单而扩展的归档格式。通过打包应用,我们可以减轻Windows上长路径名的问题,加快require的速度,并防止他人随意查看你的源代码。

🌐 After creating an application distribution, the app's source code is 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.readFilerequire 这样的 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 require extra unpacking are:

  • child_process.execFile
  • child_process.execFileSync
  • fs.open
  • fs.openSync
  • process.dlopen - 由 require 在本地模块上使用

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.execchild_process.spawnchild_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.

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

🌐 This is because exec and spawn accept command instead of file as input, and commands 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.unpacked 的文件夹被创建,同时还有 app.asar 文件。它包含了解压后的文件,应该与 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.