上QQ阅读APP看书,第一时间看更新
4.4 Mac应用升级原理
默认情况下,electron-builder使用Squirrel.Mac(https://github.com/Squirrel/Squirrel.Mac)打包工具生成Mac下的安装包,所以electron-updater在Mac环境下的升级逻辑也是基于Squirrel.Mac实现的。
当electron-updater下载并验证过安装文件后,并不能直接把安装包交给Squirrel.Mac进行升级,而是需要在本地启动一个localhost的http服务,以Squirrel.Mac要求的方式提供响应。启动http服务的代码如下所示:
import { createServer, IncomingMessage, ServerResponse } from "http" private nativeUpdater: AutoUpdater = require("electron").autoUpdater const server = createServer() function getServerUrl(): string { const address = server.address() as AddressInfo return 'http:// 127.0.0.1:${address.port}' } server.listen(0, "127.0.0.1", () => { this.nativeUpdater.setFeedURL({ url: getServerUrl(), headers: {"Cache-Control": "no-cache"}, }) this.nativeUpdater.once("error", reject) // The update has been dowloaded and is ready to be served to Squirrel this.dispatchUpdateDownloaded(event) if (this.autoInstallOnAppQuit) { // This will trigger fetching and installing the file on Squirrel side this.nativeUpdater.checkForUpdates() } })
上述代码创建http服务成功后,即把该http服务的地址设置给了Electron内置的autoUpdater模块,并调用了这个模块的checkForUpdates方法,这个方法执行过程中会请求此本地http服务的两个接口,第一个接口获得安装包的请求路径,代码如下所示:
server.on("request", (request: IncomingMessage, response: ServerResponse) => { const requestUrl = request.url!! if (requestUrl === "/") { const data = Buffer.from('{ "url": "${getServerUrl()}${fileUrl}" }') response.writeHead(200, {"Content-Type": "application/json", "Content-Length": data.length}) response.end(data) return } ...... })
第二个接口就直接响应安装包的内容,关键代码如下所示:
let errorOccurred = false import { createReadStream } from "fs" response.on("finish", () => { try { setImmediate(() => server.close()) } finally { if (!errorOccurred) { this.nativeUpdater.removeListener("error", reject) resolve([]) } } }) const readStream = createReadStream(downloadedFile) response.writeHead(200, { "Content-Type": "application/zip", "Content-Length": updateFileSize, }) readStream.pipe(response)
当安装包数据响应完成时则调用Electron内置的autoUpdater的quitAndInstall方法,退出当前应用并启动新的安装包。
以上内容就是electron-updater依赖包的内部原理。