Windows Electron 应用更新的原理是什么?揭秘 NsisUpdater

简介: 本文介绍了 Electron 应用在 Windows 中的更新原理,重点分析了 `NsisUpdater` 类的实现。该类利用 NSIS 脚本,通过初始化、检查更新、下载更新、验证签名和安装更新等步骤,确保应用的更新过程安全可靠。核心功能包括差异下载、签名验证和管理员权限处理,确保更新高效且安全。

本文首发微信公众号:前端徐徐。

大家好,我是徐徐。今天我们讲讲 Electron 应用在 windows 中的更新原理。

前言

在 Electron 中 Windows 应用的更新原理其实并不复杂,他巧妙的结合了 NSIS,专门为基于 NSIS (Nullsoft Scriptable Install System) 的 Windows 应用程序设计了相应的更新器,下面我们来详细看看具体是如何实现的吧。

源码位置

https://github1s.com/electron-userland/electron-builder/blob/master/packages/electron-updater/src/NsisUpdater.ts

整体流程概述

  1. 初始化: 通过构造函数设置更新器。
  2. 检查更新: (不在这段代码中,但是更新流程的一部分)。
  3. 下载更新: 使用 doDownloadUpdate 方法下载更新文件。
  • 可能使用差异下载 (differentialDownloadWebPackage) 来优化下载过程。
  1. 验证更新: 使用 verifySignature 方法验证下载的文件。
  2. 安装更新: 使用 doInstall 方法执行安装过程。

构造函数和签名验证设置

这里主要是在更新过程开始时,初始化更新器并设置必要的配置,为后续的更新包验证准备签名验证机制。

constructor(options?: AllPublishOptions | null, app?: AppAdapter) {
  super(options, app)
}
protected _verifyUpdateCodeSignature: verifyUpdateCodeSignature = (publisherNames: Array<string>, unescapedTempUpdateFile: string) =>
  verifySignature(publisherNames, unescapedTempUpdateFile, this._logger)
get verifyUpdateCodeSignature(): verifyUpdateCodeSignature {
  return this._verifyUpdateCodeSignature
}
set verifyUpdateCodeSignature(value: verifyUpdateCodeSignature) {
  if (value) {
    this._verifyUpdateCodeSignature = value
  }
}

核心逻辑包括:

  • 构造函数初始化更新器,设置基本配置和应用适配器。
  • _verifyUpdateCodeSignature 方法定义了默认的签名验证逻辑。
  • getter 和 setter 允许自定义签名验证方法。

下载更新

这是更新过程中的核心步骤,负责获取新版本的安装程序,确保下载的文件是完整和安全的,为后续的安装步骤准备必要的文件。

protected doDownloadUpdate(downloadUpdateOptions: DownloadUpdateOptions): Promise<Array<string>> {
  const provider = downloadUpdateOptions.updateInfoAndProvider.provider
  const fileInfo = findFile(provider.resolveFiles(downloadUpdateOptions.updateInfoAndProvider.info), "exe")!
  return this.executeDownload({
    fileExtension: "exe",
    downloadUpdateOptions,
    fileInfo,
    task: async (destinationFile, downloadOptions, packageFile, removeTempDirIfAny) => {
      const packageInfo = fileInfo.packageInfo
      const isWebInstaller = packageInfo != null && packageFile != null
      if (isWebInstaller && downloadUpdateOptions.disableWebInstaller) {
        throw newError("Unable to download new version. Web Installers are disabled", "ERR_UPDATER_WEB_INSTALLER_DISABLED")
      }
      
      // 执行下载逻辑
      if (isWebInstaller || downloadUpdateOptions.disableDifferentialDownload ||
          (await this.differentialDownloadInstaller(fileInfo, downloadUpdateOptions, destinationFile, provider, CURRENT_APP_INSTALLER_FILE_NAME))) {
        await this.httpExecutor.download(fileInfo.url, destinationFile, downloadOptions)
      }
      // 验证签名
      const signatureVerificationStatus = await this.verifySignature(destinationFile)
      if (signatureVerificationStatus != null) {
        await removeTempDirIfAny()
        throw newError(`New version is not signed by the application owner: ${signatureVerificationStatus}`, "ERR_UPDATER_INVALID_SIGNATURE")
      }
      // 处理 Web 安装程序的包下载
      if (isWebInstaller) {
        // ... (Web 安装程序包下载逻辑)
      }
    },
  })
}

核心逻辑包括:

  • 方法首先解析更新信息,找到要下载的 exe 文件。
  • 处理 Web 安装程序和普通安装程序的下载逻辑。
  • 支持差异下载和完整下载。
  • 下载完成后验证文件签名。
  • 对于 Web 安装程序,还需要下载额外的包文件。

差异下载

在下载更新时,尝试使用差异下载来减少带宽使用和下载时间,通过只下载变更的部分,提高更新效率,为大型更新包提供优化的下载方式

private async differentialDownloadWebPackage(
  downloadUpdateOptions: DownloadUpdateOptions,
  packageInfo: PackageFileInfo,
  packagePath: string,
  provider: Provider<any>
): Promise<boolean> {
  if (packageInfo.blockMapSize == null) {
    return true
  }
  try {
    const downloadOptions: DifferentialDownloaderOptions = {
      newUrl: new URL(packageInfo.path),
      oldFile: path.join(this.downloadedUpdateHelper!.cacheDir, CURRENT_APP_PACKAGE_FILE_NAME),
      logger: this._logger,
      newFile: packagePath,
      requestHeaders: this.requestHeaders,
      isUseMultipleRangeRequest: provider.isUseMultipleRangeRequest,
      cancellationToken: downloadUpdateOptions.cancellationToken,
    }
    if (this.listenerCount(DOWNLOAD_PROGRESS) > 0) {
      downloadOptions.onProgress = it => this.emit(DOWNLOAD_PROGRESS, it)
    }
    await new FileWithEmbeddedBlockMapDifferentialDownloader(packageInfo, this.httpExecutor, downloadOptions).download()
  } catch (e: any) {
    this._logger.error(`Cannot download differentially, fallback to full download: ${e.stack || e}`)
    return process.platform === "win32"
  }
  return false
}

核心逻辑包括:

  • 检查是否支持差异下载(通过 blockMapSize)。
  • 设置差异下载的选项,包括新旧文件路径、请求头等。
  • 使用 FileWithEmbeddedBlockMapDifferentialDownloader 执行差异下载。
  • 如果差异下载失败,在 Windows 平台上回退到完整下载。

签名验证

在下载完成后,验证更新文件的真实性和完整性,防止安装未经授权或被篡改的更新。

private async verifySignature(tempUpdateFile: string): Promise<string | null> {
  let publisherName: Array<string> | string | null
  try {
    publisherName = (await this.configOnDisk.value).publisherName
    if (publisherName == null) {
      return null
    }
  } catch (e: any) {
    if (e.code === "ENOENT") {
      // no app-update.yml
      return null
    }
    throw e
  }
  return await this._verifyUpdateCodeSignature(Array.isArray(publisherName) ? publisherName : [publisherName], tempUpdateFile)
}

核心逻辑包括:

  • 从磁盘配置中读取发布者名称。
  • 如果没有配置文件或发布者名称,则跳过验证。
  • 调用 _verifyUpdateCodeSignature 方法进行实际的签名验证。

安装

这是更新过程的最后一步,负责执行实际的安装,确保安装程序以正确的权限和参数运行,处理安装过程中可能出现的各种情况和错误。

protected doInstall(options: InstallOptions): boolean {
  const args = ["--updated"]
  if (options.isSilent) {
    args.push("/S")
  }
  if (options.isForceRunAfter) {
    args.push("--force-run")
  }
  if (this.installDirectory) {
    args.push(`/D=${this.installDirectory}`)
  }
  const packagePath = this.downloadedUpdateHelper == null ? null : this.downloadedUpdateHelper.packageFile
  if (packagePath != null) {
    args.push(`--package-file=${packagePath}`)
  }
  const callUsingElevation = (): void => {
    this.spawnLog(path.join(process.resourcesPath, "elevate.exe"), [options.installerPath].concat(args)).catch(e => this.dispatchError(e))
  }
  if (options.isAdminRightsRequired) {
    this._logger.info("isAdminRightsRequired is set to true, run installer using elevate.exe")
    callUsingElevation()
    return true
  }
  this.spawnLog(options.installerPath, args).catch((e: Error) => {
    // 错误处理逻辑
    if (errorCode === "UNKNOWN" || errorCode === "EACCES") {
      callUsingElevation()
    } else if (errorCode === "ENOENT") {
      require("electron").shell.openPath(options.installerPath).catch((err: Error) => this.dispatchError(err))
    } else {
      this.dispatchError(e)
    }
  })
  return true
}

核心逻辑包括:

  • 根据提供的选项构建安装参数。
  • 支持静默安装、强制运行、自定义安装目录等选项。
  • 处理需要管理员权限的情况,使用 elevate.exe 提升权限。
  • 处理各种安装错误,包括权限问题和文件不存在的情况。

结语

通过简单的分析源码发现,Electron 应用在 windows 上的更新其实也不难,这个NsisUpdater 类不仅仅是一个简单的文件下载和替换工具,而是一个精心设计的系统,它考虑到了软件更新过程中的诸多方面,当然底层的 NSIS 程序帮助了我们不少的忙,如果在往下探的话,可以研究一下 NSIS,具体可以参考:https://nsis.sourceforge.io/Main_Page

相关文章
|
3天前
|
存储 人工智能 弹性计算
阿里云弹性计算_加速计算专场精华概览 | 2024云栖大会回顾
2024年9月19-21日,2024云栖大会在杭州云栖小镇举行,阿里云智能集团资深技术专家、异构计算产品技术负责人王超等多位产品、技术专家,共同带来了题为《AI Infra的前沿技术与应用实践》的专场session。本次专场重点介绍了阿里云AI Infra 产品架构与技术能力,及用户如何使用阿里云灵骏产品进行AI大模型开发、训练和应用。围绕当下大模型训练和推理的技术难点,专家们分享了如何在阿里云上实现稳定、高效、经济的大模型训练,并通过多个客户案例展示了云上大模型训练的显著优势。
|
7天前
|
存储 人工智能 调度
阿里云吴结生:高性能计算持续创新,响应数据+AI时代的多元化负载需求
在数字化转型的大潮中,每家公司都在积极探索如何利用数据驱动业务增长,而AI技术的快速发展更是加速了这一进程。
|
4天前
|
人工智能 运维 双11
2024阿里云双十一云资源购买指南(纯客观,无广)
2024年双十一,阿里云推出多项重磅优惠,特别针对新迁入云的企业和初创公司提供丰厚补贴。其中,36元一年的轻量应用服务器、1.95元/小时的16核60GB A10卡以及1元购域名等产品尤为值得关注。这些产品不仅价格亲民,还提供了丰富的功能和服务,非常适合个人开发者、学生及中小企业快速上手和部署应用。
|
12天前
|
人工智能 弹性计算 文字识别
基于阿里云文档智能和RAG快速构建企业"第二大脑"
在数字化转型的背景下,企业面临海量文档管理的挑战。传统的文档管理方式效率低下,难以满足业务需求。阿里云推出的文档智能(Document Mind)与检索增强生成(RAG)技术,通过自动化解析和智能检索,极大地提升了文档管理的效率和信息利用的价值。本文介绍了如何利用阿里云的解决方案,快速构建企业专属的“第二大脑”,助力企业在竞争中占据优势。
|
14天前
|
自然语言处理 数据可视化 前端开发
从数据提取到管理:合合信息的智能文档处理全方位解析【合合信息智能文档处理百宝箱】
合合信息的智能文档处理“百宝箱”涵盖文档解析、向量化模型、测评工具等,解决了复杂文档解析、大模型问答幻觉、文档解析效果评估、知识库搭建、多语言文档翻译等问题。通过可视化解析工具 TextIn ParseX、向量化模型 acge-embedding 和文档解析测评工具 markdown_tester,百宝箱提升了文档处理的效率和精确度,适用于多种文档格式和语言环境,助力企业实现高效的信息管理和业务支持。
3935 2
从数据提取到管理:合合信息的智能文档处理全方位解析【合合信息智能文档处理百宝箱】
|
3天前
|
算法 安全 网络安全
阿里云SSL证书双11精选,WoSign SSL国产证书优惠
2024阿里云11.11金秋云创季活动火热进行中,活动月期间(2024年11月01日至11月30日)通过折扣、叠加优惠券等多种方式,阿里云WoSign SSL证书实现优惠价格新低,DV SSL证书220元/年起,助力中小企业轻松实现HTTPS加密,保障数据传输安全。
494 3
阿里云SSL证书双11精选,WoSign SSL国产证书优惠
|
10天前
|
安全 数据建模 网络安全
2024阿里云双11,WoSign SSL证书优惠券使用攻略
2024阿里云“11.11金秋云创季”活动主会场,阿里云用户通过完成个人或企业实名认证,可以领取不同额度的满减优惠券,叠加折扣优惠。用户购买WoSign SSL证书,如何叠加才能更加优惠呢?
985 3
|
7天前
|
机器学习/深度学习 存储 人工智能
白话文讲解大模型| Attention is all you need
本文档旨在详细阐述当前主流的大模型技术架构如Transformer架构。我们将从技术概述、架构介绍到具体模型实现等多个角度进行讲解。通过本文档,我们期望为读者提供一个全面的理解,帮助大家掌握大模型的工作原理,增强与客户沟通的技术基础。本文档适合对大模型感兴趣的人员阅读。
391 15
白话文讲解大模型| Attention is all you need
|
7天前
|
算法 数据建模 网络安全
阿里云SSL证书2024双11优惠,WoSign DV证书220元/年起
2024阿里云11.11金秋云创季火热进行中,活动月期间(2024年11月01日至11月30日),阿里云SSL证书限时优惠,部分证书产品新老同享75折起;通过优惠折扣、叠加满减优惠券等多种方式,阿里云WoSign SSL证书将实现优惠价格新低,DV SSL证书220元/年起。
559 5
|
3天前
|
安全 网络安全
您有一份网络安全攻略待领取!!!
深入了解如何保护自己的云上资产,领取超酷的安全海报和定制鼠标垫,随时随地提醒你保持警惕!
689 1
您有一份网络安全攻略待领取!!!