跳到主要內容

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 封存檔視為虛擬目錄,並將其中的檔案視為檔案系統中的一般檔案。

例如,假設我們在 /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 的偽造 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.unpacked 的資料夾,以及 app.asar 檔案。它包含未封裝的檔案,應與 app.asar 封存檔一起發布。