当前位置: 首页 > news >正文

Electron-updater + Electron-builder + IIS + NSIS + Blockmap 完整增量更新方案

Electron-updater + Electron-builder + IIS + NSIS + Blockmap 完整增量更新方案

将这些工具结合使用可以实现高效、可靠的增量更新机制,其中 Blockmap 技术能显著减小更新包体积,提升用户体验。以下是完整实现方案:

方案概述

  • Electron-builder:负责打包应用并生成支持 Blockmap 的安装包和增量更新包
  • Electron-updater:客户端更新引擎,利用 Blockmap 实现高效增量更新
  • IIS:托管更新文件(包括安装包、Blockmap 和版本信息)
  • NSIS:生成 Windows 安装程序,支持自定义安装流程
  • Blockmap:二进制块映射技术,用于精确计算和传输文件差异

1. 环境准备

安装依赖

# 核心依赖
npm install electron-updater --save# 开发依赖
npm install electron --save-dev
npm install electron-builder --save-dev

2. 项目配置

package.json 完整配置

{"name": "electron-advanced-updater","version": "1.0.0","main": "src/main.js","scripts": {"start": "electron .","build": "electron-builder --win","build:dir": "electron-builder --win --dir","release": "electron-builder --win --publish always"},"build": {"productName": "ElectronAdvancedUpdater","appId": "com.example.electronadvancedupdater","copyright": "Copyright © 2023 Your Company","directories": {"output": "dist","buildResources": "build"},"generateBlockmap": true,  // 强制生成blockmap文件"win": {"target": [{"target": "nsis","arch": ["x64","ia32"]}],"publish": [{"provider": "generic","url": "http://your-iis-server/update/"}],"blockmap": {"compression": "deflate"  // blockmap压缩算法}},"nsis": {"oneClick": false,"allowToChangeInstallationDirectory": true,"installerIcon": "build/icon.ico","uninstallerIcon": "build/icon.ico","installerHeaderIcon": "build/icon.ico","createDesktopShortcut": true,"createStartMenuShortcut": true,"shortcutName": "Electron Advanced Updater","include": "build/installer.nsh"  // 自定义NSIS脚本},"publish": {"provider": "generic","url": "http://your-iis-server/update/"},"files": ["src/**/*","node_modules/**/*","package.json"],"extraMetadata": {"main": "src/main.js"}},"devDependencies": {"electron": "^26.0.0","electron-builder": "^24.6.0"},"dependencies": {"electron-updater": "^6.1.4"}
}

3. 代码实现

主进程更新逻辑 (src/main.js)

const { app, BrowserWindow, ipcMain, dialog } = require('electron');
const { autoUpdater } = require('electron-updater');
const path = require('path');
const log = require('electron-log');// 配置日志
log.transports.file.level = 'info';
autoUpdater.logger = log;
console.log = log.log; // 重定向console日志到文件let mainWindow;function createWindow() {mainWindow = new BrowserWindow({width: 1024,height: 768,webPreferences: {preload: path.join(__dirname, 'preload.js'),contextIsolation: true,nodeIntegration: false}});mainWindow.loadFile(path.join(__dirname, '../public/index.html'));// 开发环境下打开开发者工具if (process.env.NODE_ENV === 'development') {mainWindow.webContents.openDevTools();}mainWindow.on('closed', () => {mainWindow = null;});
}// 发送状态到渲染进程
function sendStatusToWindow(status, progress = null) {if (mainWindow && mainWindow.webContents) {mainWindow.webContents.send('update-status', {status,progress});}
}// 配置并检查更新
function configureAndCheckUpdates() {// 配置更新源autoUpdater.setFeedURL({provider: 'generic',url: 'http://your-iis-server/update/'});// 更新事件监听autoUpdater.on('checking-for-update', () => {sendStatusToWindow('正在检查更新...');});autoUpdater.on('update-available', (info) => {sendStatusToWindow(`发现新版本 v${info.version},准备下载...`);});autoUpdater.on('update-not-available', (info) => {sendStatusToWindow('当前已是最新版本');});autoUpdater.on('error', (err) => {sendStatusToWindow(`更新错误: ${err.message}`);log.error('更新错误:', err);});autoUpdater.on('download-progress', (progressObj) => {const progress = Math.round(progressObj.percent);sendStatusToWindow(`正在下载: ${progress}%`, progress);});autoUpdater.on('update-downloaded', (info) => {sendStatusToWindow('更新已下载完成,准备安装');// 询问用户是否立即安装dialog.showMessageBox({type: 'info',title: '更新准备就绪',message: `新版本 v${info.version} 已下载完成,是否立即重启应用以应用更新?`,buttons: ['立即重启', '稍后重启']}).then((result) => {if (result.response === 0) {autoUpdater.quitAndInstall(false, true);}});});// 检查更新autoUpdater.checkForUpdates().catch(err => {log.error('检查更新失败:', err);});
}// 监听渲染进程请求
ipcMain.on('check-for-updates', () => {configureAndCheckUpdates();
});ipcMain.on('get-current-version', (event) => {event.returnValue = app.getVersion();
});// 应用生命周期管理
app.on('ready', () => {createWindow();// 启动后延迟检查更新,避免影响启动性能setTimeout(() => {configureAndCheckUpdates();}, 3000);
});app.on('window-all-closed', () => {if (process.platform !== 'darwin') {app.quit();}
});app.on('activate', () => {if (BrowserWindow.getAllWindows().length === 0) {createWindow();}
});

预加载脚本 (src/preload.js)

{const { contextBridge, ipcRenderer } = require('electron');contextBridge.exposeInMainWorld('electronUpdater', {checkForUpdates: () => ipcRenderer.send('check-for-updates'),getCurrentVersion: () => ipcRenderer.sendSync('get-current-version'),onUpdateStatus: (callback) => {ipcRenderer.on('update-status', (event, status) => callback(status));}});
}

渲染进程页面 (public/index.html)


<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>Electron 高级更新示例</title><style>body {font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;margin: 20px;color: #333;}.update-container {max-width: 600px;margin: 50px auto;padding: 20px;border-radius: 8px;box-shadow: 0 2px 10px rgba(0,0,0,0.1);}.version-info {margin-bottom: 20px;color: #666;}.update-status {padding: 15px;border-radius: 4px;background-color: #f5f5f5;min-height: 40px;margin: 15px 0;}.progress-container {height: 8px;background-color: #eee;border-radius: 4px;overflow: hidden;margin: 10px 0;display: none;}.progress-bar {height: 100%;background-color: #0078d7;width: 0%;transition: width 0.3s ease;}button {background-color: #0078d7;color: white;border: none;padding: 10px 20px;border-radius: 4px;cursor: pointer;font-size: 14px;}button:hover {background-color: #005a9e;}</style>
</head>
<body><div class="update-container"><h1>Electron 高级更新示例</h1><div class="version-info">当前版本: <span id="current-version"></span></div><button id="check-update-btn">检查更新</button><div class="update-status" id="update-status">准备就绪</div><div class="progress-container" id="progress-container"><div class="progress-bar" id="progress-bar"></div></div></div><script>// 显示当前版本document.getElementById('current-version').textContent = window.electronUpdater.getCurrentVersion();// 检查更新按钮document.getElementById('check-update-btn').addEventListener('click', () => {document.getElementById('check-update-btn').disabled = true;document.getElementById('update-status').textContent = '正在发起更新检查...';window.electronUpdater.checkForUpdates();});// 监听更新状态window.electronUpdater.onUpdateStatus(({ status, progress }) => {document.getElementById('update-status').textContent = status;// 处理进度显示const progressContainer = document.getElementById('progress-container');const progressBar = document.getElementById('progress-bar');if (progress !== null) {progressContainer.style.display = 'block';progressBar.style.width = `${progress}%`;// 下载完成后重新启用按钮if (progress === 100) {document.getElementById('check-update-btn').disabled = false;}} else {// 没有进度信息时隐藏进度条progressContainer.style.display = 'none';document.getElementById('check-update-btn').disabled = false;}});</script>
</body>
</html>

自定义 NSIS 脚本 (build/installer.nsh)

; 引入必要的库
!include "MUI2.nsh"
!include "LogicLib.nsh"
!include "FileFunc.nsh"
!include "electron-builder.nsh"; 配置安装程序
Name "Electron Advanced Updater"
OutFile "ElectronAdvancedUpdater-Setup.exe"
InstallDir "$PROGRAMFILES\ElectronAdvancedUpdater"; 定义安装界面
!define MUI_ABORTWARNING
!define MUI_ICON "${BUILD_RESOURCES_DIR}\icon.ico"
!define MUI_UNICON "${BUILD_RESOURCES_DIR}\icon.ico"; 欢迎页面
!insertmacro MUI_PAGE_WELCOME
; 安装目录选择页面
!insertmacro MUI_PAGE_DIRECTORY
; 安装进度页面
!insertmacro MUI_PAGE_INSTFILES
; 完成页面
!define MUI_FINISHPAGE_RUN "$INSTDIR\ElectronAdvancedUpdater.exe"
!insertmacro MUI_PAGE_FINISH; 卸载页面
!insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH; 语言设置
!insertmacro MUI_LANGUAGE "SimpChinese"
!insertmacro MUI_LANGUAGE "English"; 安装前检查
Function .onInit; 检查应用是否正在运行${If} ${RunningX64}StrCpy $R0 "ElectronAdvancedUpdater.exe"${Else}StrCpy $R0 "ElectronAdvancedUpdater.exe"${EndIf}nsProcess::FindProcess "$R0"Pop $R1${If} $R1 == 0MessageBox MB_ICONEXCLAMATION "应用程序正在运行,请先关闭再继续安装。" /SD IDOKAbort${EndIf}; 调用electron-builder的初始化函数!insertmacro electron-builder::onInit
FunctionEnd; 安装完成后的操作
Function .onInstSuccess; 可以在这里添加注册系统事件、创建额外快捷方式等操作
FunctionEnd; 主安装逻辑
Section "MainSection" SEC01; 包含electron-builder的安装逻辑!insertmacro electron-builder::mainSection
SectionEnd; 卸载前的清理操作
Section "Uninstall"; 停止所有相关进程${nsProcess::KillProcess} "ElectronAdvancedUpdater.exe" ""Pop $0; 调用electron-builder的卸载逻辑!insertmacro electron-builder::uninstallSection; 删除残留目录RMDir /r "$INSTDIR"
SectionEnd; 安装完成页面设置
!define MUI_FINISHPAGE_TITLE "安装完成"
!define MUI_FINISHPAGE_DESCRIPTION "Electron Advanced Updater 已成功安装。"

4. IIS 服务器配置

目录结构

在 IIS 网站根目录下创建如下结构:

/update//win32-x64/ElectronAdvancedUpdater-1.0.0 Setup.exe.blockmapElectronAdvancedUpdater-1.1.0 Setup.exeElectronAdvancedUpdater-1.1.0 Setup.exe.blockmaplatest.yml/win32-ia32/; 同上,32位版本文件

MIME 类型配置

在 IIS 管理器中为网站添加以下 MIME 类型:

扩展名MIME 类型说明
.nupkgapplication/zip增量更新包
.ymltext/yaml版本信息文件
.blockmapapplication/octet-streamBlockmap元数据
.exeapplication/exe安装程序

权限配置

确保 update 目录授予 IIS_IUSRS 组读取权限,允许匿名访问。

5. 构建与发布流程

首次发布 (v1.0.0)

  1. 确保 package.json 中版本号为 1.0.0
  2. 构建安装包:
    npm run build
    
  3. 构建成功后,将 dist 目录下的以下文件上传到 IIS 对应目录:
    • win-unpacked 目录(可选,用于调试)
    • ElectronAdvancedUpdater-1.0.0 Setup.exe
    • ElectronAdvancedUpdater-1.0.0 Setup.exe.blockmap
    • ElectronAdvancedUpdater-1.0.0-full.nupkg
    • latest.yml

发布增量更新 (v1.1.0)

  1. 更新 package.json 中的版本号为 1.1.0
  2. 构建新版本:
    npm run build
    
  3. electron-builder 会自动生成增量包(*-delta.nupkg
  4. 将新生成的文件上传到 IIS 服务器:
  • ElectronAdvancedUpdater-1.0.0 Setup.exe.blockmap
  • ElectronAdvancedUpdater-1.1.0 Setup.exe
  • ElectronAdvancedUpdater-1.1.0 Setup.exe.blockmap
  • latest.yml(更新版本信息)

6. Blockmap 工作原理验证

  1. 检查构建输出
    确认 dist 目录中存在 .blockmap 文件,例如:

    ElectronAdvancedUpdater-1.1.0 Setup.exe.blockmap
    
  2. 查看更新日志
    在应用数据目录下的日志文件中(如 %APPDATA%\ElectronAdvancedUpdater\logs\main.log),确认有 Blockmap 相关日志:

    [autoUpdater] Block map signature verification passed
    [autoUpdater] Calculating diff with block map
    [autoUpdater] Downloading block maps for differential update
    
  3. 验证增量包大小
    比较全量包(-full.nupkg)和增量包(-delta.nupkg)的大小,增量包应显著 smaller(通常小 50-80%)。

7. 常见问题与解决方案

问题 1:Blockmap 文件未生成

  • 检查:确认 package.jsongenerateBlockmap 设为 true
  • 解决:删除 node_modulesdist 目录,重新安装依赖并构建

问题 2:增量更新失败,总是下载全量包

  • 检查
    • 确认所有历史版本的安装包和 blockmap 文件都已上传到服务器
    • 检查 latest.yml 文件是否正确包含所有版本信息
  • 解决:确保版本号严格遵循语义化版本规范,且所有文件路径正确

问题 3:IIS 服务器无法访问 blockmap 文件

  • 检查:使用浏览器直接访问 blockmap 文件 URL,确认能正常下载
  • 解决:重新配置 .blockmap 的 MIME 类型为 application/octet-stream

问题 4:更新后应用无法启动

  • 检查:查看应用日志,确认是否有文件权限问题
  • 解决:在 NSIS 脚本中添加适当的权限设置,或在更新前关闭所有应用进程

总结

通过整合 Electron-updater、Electron-builder、IIS、NSIS 和 Blockmap 技术,我们实现了一个高效的增量更新方案:

  • Blockmap 技术显著减小了更新包体积,节省带宽和下载时间
  • Electron-updater 提供了可靠的更新检测和安装机制
  • IIS 服务器提供了稳定的更新文件托管服务
  • NSIS 确保了良好的安装和更新体验
http://www.lryc.cn/news/610935.html

相关文章:

  • GPT-1、GPT-2、GPT-3 的区别和联系
  • 7、Redis队列Stream和单线程及多线程模型
  • 人工智能领域、图欧科技、IMYAI智能助手2025年4月更新月报
  • 【RK3576】【Android14】Uboot下fastboot命令支持
  • 创维智能融合终端DT741_移动版_S905L3芯片_安卓9_线刷固件包
  • CTF-XXE 漏洞解题思路总结
  • 测试开发:Python+Django实现接口测试工具
  • Python-初学openCV——图像预处理(七)——亮度变换、形态学变换
  • ThingsKit Edge是什么?
  • 从零实现富文本编辑器#6-浏览器选区与编辑器选区模型同步
  • 数据结构 | 树的秘密
  • 在Linux上部署tomcat、nginx
  • CRT调试堆检测:从原理到实战的资源泄漏排查指南
  • Apifox使用mock模仿后端返回数据
  • JumpServer 堡垒机全流程搭建指南及常见问题解决方案
  • Redis存储string里面embstr和raw格式区别
  • 【Linux】特效爆满的Vim的配置方法 and make/Makefile原理
  • 【01】OpenCV C++实战篇——基于多项式插值的亚像素边缘定位算法
  • Occ3D: A Large-Scale 3D Occupancy Prediction Benchmark for Autonomous Driving
  • Python爬虫实战:研究weiboSpider技术,构建新浪微博数据采集系统
  • 多层Model更新多层ListView
  • RHCA05--进程管理与文件系统管理
  • 数据结构(01)—— 数据结构的基本概念
  • 应用科普 | 漫谈6G通信的未来
  • 【技术教程】如何将 ONLYOFFICE 文档连接到 Confluence
  • 坚鹏:AI智能体软件是知行学成为AI智能体创新应用引领者的抓手
  • Fiddler 中文版实战指南,如何构建高效的 API 调试工作流?
  • Z20K118库中寄存器及其库函数封装-ADC库
  • Linux操作系统从入门到实战(十三)版本控制器Git基础概念讲解
  • 自抗扰ADCR--跟踪微分器的作用