深入浅出Electron:原理、工程与实践
上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依赖包的内部原理。