跳到主要內容

ASAR 封存檔

在建立應用程式發佈後,應用程式的原始碼通常會被捆綁到一個 ASAR 封存檔中,這是一種為 Electron 應用程式設計的簡單擴展封存格式。透過捆綁應用程式,我們可以減輕 Windows 上長路徑名稱的問題、加速 require 並隱藏您的原始碼,使其免於粗略的檢查。

捆綁的應用程式會在虛擬檔案系統中執行,而且大多數 API 會正常運作,但在某些情況下,您可能因為一些注意事項而需要明確地處理 ASAR 封存檔。

使用 ASAR 封存檔

在 Electron 中,有兩組 API:Node.js 提供的 Node API 和 Chromium 提供的 Web API。這兩種 API 都支援從 ASAR 封存檔讀取檔案。

Node API

透過 Electron 中的特殊修補程式,像 fs.readFilerequire 這類的 Node API 會將 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')

您也可以使用 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 模組,該模組提供不支援 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 封存檔中的目錄。將它們作為某些 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 的虛假狀態資訊

fs.stat 及其在 asar 封存檔中檔案上的朋友傳回的 Stats 物件是透過猜測產生的,因為這些檔案不存在於檔案系統中。因此,除了取得檔案大小和檢查檔案類型之外,您不應信任 Stats 物件。

執行 ASAR 封存檔內的二進位檔

有些 Node API 可以執行二進位檔,例如 child_process.execchild_process.spawnchild_process.execFile,但只有 execFile 支援執行 ASAR 封存檔內的二進位檔。

這是因為 execspawn 接受 command 而不是 file 作為輸入,而 command 是在 shell 下執行的。沒有可靠的方法來判斷命令是否使用 asar 封存檔中的檔案,即使我們這樣做了,我們也不能確定是否可以在不產生副作用的情況下替換命令中的路徑。

將未解壓縮的檔案新增至 ASAR 封存檔

如上所述,某些 Node API 在呼叫時會將檔案解壓縮到檔案系統。除了效能問題外,各種防毒掃描程式可能會因這種行為而觸發。

作為一種解決方法,您可以使用 --unpack 選項保留各種未解壓縮的檔案。在以下範例中,原生 Node.js 模組的共用程式庫將不會被壓縮

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

執行命令後,您會注意到與 app.asar 檔案一起建立了一個名為 app.asar.unpacked 的資料夾。它包含未解壓縮的檔案,應與 app.asar 封存檔一起運送。