如何使用GeneralUpdte构建客户端自动升级功能
一、概要
本篇文章將向各位小伙伴介紹GeneralUpdate組件的使用,幫助第一次接觸開發者快速上手應用在自己或企業項目中。如果本篇文章對您有幫助,希望幫忙點一下star。感謝各位開發者的支持。
幫助文檔
講解視頻:https://www.bilibili.com/video/BV1aX4y137dd
官方網站:http://justerzhu.cn/
相關工具:GeneralUpdate.PacketTool (該工具使用avalonia編寫,可在linux、windows、mac操作系統使用)
github release: https://github.com/WELL-E/AutoUpdater/releases/tag/GeneralUpdateTool
gitee release: https://gitee.com/Juster-zhu/GeneralUpdate/releases/GeneralupdateTool
Nuget版本管理參考標準:https://docs.microsoft.com/zh-cn/nuget/concepts/package-versioning
應用程序集版本管理參考標準:https://docs.microsoft.com/zh-cn/dotnet/standard/assembly/versioning (被組件更新的客戶端程序,說通俗點就是你公司的產品;組件的操作將按照這個標準執行。)
開源地址
https://github.com/WELL-E/AutoUpdater
https://gitee.com/Juster-zhu/GeneralUpdate
Q&A
(1)主程序和升級程序之間是否支持相互升級?
答:支持。
(2)是否需要開發者寫代碼關閉進程的時機或者其他代碼?
答:不需要,組件已經將整個更新流程考慮到了。所以除了組件代碼以外,不需要開發者額外多寫任何輔助代碼。
(3)更新程序是否需要和主程序放在同一個目錄下?
答:是的,需要。但一定要保持升級程序不能引用主程序的里的任何代碼。否則會更新失敗。
(4)更新完成之后會刪除更新包的補丁文件嗎?
答:會的,組件更新完成之后會保證文件列表干凈,不會出現冗余文件污染、磁盤空間占用的情況。
(5)可以運用在服務端嗎?就是服務與服務之間的升級。
答:理論上支持的,作者沒有實際這么使用過。據反饋有的小伙伴已經這么干了。本次分享是針對C/S架構的場景。
(6)怎么獲取更新包的MD5碼?
答:使用項目源碼里的,AutoUpdate.MD5工程。
(7)怎么制作一個更新包?
答:使用GeneralUpdate.PacketTool工具生成即可。在源碼倉庫的release中可以看到打包好的安裝程序。
(8)關于組件的其他內容如何了解到?
答:可以通過官方網站、或者相關Q群、以及我gitee或github的issue中與我交流。
(9)下載包解壓在C盤下Program Files (x86)時,沒有權限操作怎么處理?
答:https://gitee.com/Juster-zhu/GeneralUpdate/issues/I4ZKQ4
(10)更新文件較小時,下載速度顯示為:0B/S 。
答:https://gitee.com/Juster-zhu/GeneralUpdate/issues/I3POMG
二、詳細內容
在開始講解使用之前,我們先需要搞明白GeneralUpdate更新體系中的一些基礎概念、名詞。
(1)名詞解釋
client:是指你的主應用程序,是被更新的客戶端。也就是你公司的產品(假設項目結構如上)。它將需要在nuget平臺安裝GeneralUpdate.ClientCore。
upgrade:是指升級程序,它是一個獨立的進程。需要和clinet放在同一個目錄下,在使用的過程中不可以和任何業務關聯、必須保持獨立引用(項目結構如上)。有人會問我不保持會怎么樣?會因為其他組件引用、文件占用更新失敗。它將需要在nuget平臺安裝GeneralUpdate.Core。
server:是指服務端應用(asp.net)將提供版本更新信息、版本驗證信息用來判斷是否需要更新以及更新包下載地址。它將需要在nuget平臺安裝GeneralUpdate.AspNetCore。
SQL:關于server端需要用的sql表的腳本(mysql)已經寫好了,運行以上腳本即可創建表。
| md5 | varchar | string | 更新包的MD5碼,也是唯一標識 |
| pubtime | int | int | 更新包發布時間戳(10位) |
| name | varchar | string | 更新包名稱 |
| url | varchar | string | 下載地址 |
| version | varchar | string | 版本號(1.0.0.0) |
| clienttype | int | int | 1:客戶端 2:升級程序 |
(2)更新流程
基于以上的了解,我們再來看更新流程將會很清晰,思路清晰有助于我們使用。
第一步
client啟動后將會向服務器發送http請求,確認upgrade是否需要更新。
第二步
如果upgrade需要更新將會下載upgrade的更新包并更新。
第三步
如果client發現upgrade不需要更新或者upgrade更新完畢之后,那么將會直接通過進程啟動upgrade獨立進程的應用程序。(也就是上面為什么需要保持引用獨立)
第四步
upgrade被啟動之后,會自動去請求client的更新包。用于更新client的內容;
第五步
在client、upgrade請求更新期間,server將會起到關鍵作用。提供版本更新信息、版本驗證信息用來判斷是否需要更新以及更新包下載地址。
第六步
server響應upgrade的請求后,upgrade將執行更新client的操作。
第七步
更新完成之后upgrade通過進程啟動client。
第八步
client被啟動之后,完成更新。(流程結束)
三、編碼
應用GeneralUpdate組件總共分為,三個部分Client 、Upgrade、Server。
1.1 Client的應用
安裝完成之后,將會在nuget包引用中看到這些內容。
接下來就可以寫代碼了,在最新版本中簡化了啟動配置。(如果需要自定義配置則參考:https://gitee.com/Juster-zhu/GeneralUpdate/blob/master/src/AutoUpdate.ClientCore/MainWindow.xaml.cs)
到這里基礎的功能代碼已完成,剩下的事件回傳的內容根據需要使用即可。推薦用法為:將事件回傳參數在客戶端中用獨立遮罩層類似于“轉圈圈的”界面顯示升級進度信息,或者用日志記錄下來。
1.2Client的應用(非必要)
訂閱接收,Server端的最新版本推送。
private const string baseUrl = @"http://127.0.0.1:5001",hubName = "versionhub";public MainViewModel(){InitializeComponent();InitVersionHub();}#region VersionHub/// <summary>/// Subscription server push version message./// </summary>private void InitVersionHub(){VersionHub<string>.Instance.Subscribe($"{ baseUrl }/{ hubName }", "TESTNAME", new Action<string>(GetMessage));}private void GetMessage(string msg){TxtMessage.Text = msg;//這里接收推送的內容跟服務端約定好能解析即可,也可以在這里啟動更新。}#endregion VersionHub到這里為止,clinet的應用分享已完成。
2.1 Upgrade的應用
安裝完成之后,將會在nuget包引用中看到這些內容。
接下來就可以寫代碼了,和ClientCore不同的是它不在需要配置url等內容將從進程傳參中拿到RemoteAddressBase64的內容(內容是自動生成好的不需要關心)。
首先需要找到app.cs文件:
然后修改代碼如下,這里是為了拿到client端進程傳遞過來的配置參數:
public partial class App : Application{protected override void OnStartup(StartupEventArgs e){MainWindow window = new MainWindow(e.Args[0]);window.ShowDialog();base.OnStartup(e);}}拿到的base64的示例內容如下:
eyJBcHBUeXBlIjoxLCJBcHBOYW1lIjoiQXV0b1VwZGF0ZS5DbGllbnRDb3JlIiwiTWFpbkFwcE5hbWUiOm51bGwsIkluc3RhbGxQYXRoIjoiRDpcXFVwZGF0ZXRlc3RfaHViXFxSdW5fYXBwIiwiQ2xpZW50VmVyc2lvbiI6IjEuMS4xIiwiTGFzdFZlcnNpb24iOiI5LjEuMy4wIiwiVXBkYXRlTG9nVXJsIjpudWxsLCJJc1VwZGF0ZSI6ZmFsc2UsIlVwZGF0ZVVybCI6bnVsbCwiVmFsaWRhdGVVcmwiOm51bGwsIk1haW5VcGRhdGVVcmwiOiJodHRwOi8vMTI3LjAuMC4xOjUwMDEvdmVyc2lvbnMvMS8xLjEuMS4xIiwiTWFpblZhbGlkYXRlVXJsIjoiaHR0cDovLzEyNy4wLjAuMTo1MDAxL3ZhbGlkYXRlLzEvMS4xLjEuMSIsIkNvbXByZXNzRW5jb2RpbmciOjcsIkNvbXByZXNzRm9ybWF0IjoiLnppcCIsIkRvd25sb2FkVGltZU91dCI6NjAsIlVwZGF0ZVZlcnNpb25zIjpbeyJQdWJUaW1lIjoxNjI2NzExNzYwLCJOYW1lIjpudWxsLCJNRDUiOiI1ZmI3NWU0NGQ3YzQ1ZTNmYzlkNmFhNDdjMDVhMGU5YSIsIlZlcnNpb24iOiI5LjEuMy4wIiwiVXJsIjpudWxsLCJJc1VuWmlwIjpmYWxzZX1dfQ==MainViewModel.cs代碼:
internal class MainViewModel : BaseViewModel{private string _tips1, _tips2, _tips3, _tips4, _tips5, _tips6;private double _progressVal, _progressMin, _progressMax;public MainViewModel(string args){ProgressMin = 0;Task.Run(async () =>{var bootStrap = new GeneralUpdateBootstrap();bootStrap.MutiAllDownloadCompleted += OnMutiAllDownloadCompleted;bootStrap.MutiDownloadCompleted += OnMutiDownloadCompleted;bootStrap.MutiDownloadError += OnMutiDownloadError;bootStrap.MutiDownloadProgressChanged += OnMutiDownloadProgressChanged;bootStrap.MutiDownloadStatistics += OnMutiDownloadStatistics;bootStrap.Exception += OnException;bootStrap.Strategy<DefaultStrategy>().Option(UpdateOption.Encoding, Encoding.Default).Option(UpdateOption.DownloadTimeOut, 60).Option(UpdateOption.Format, "zip").RemoteAddressBase64(args);await bootStrap.LaunchTaskAsync();});}public string Tips1 { get => _tips1; set => SetProperty(ref _tips1, value); }public string Tips2 { get => _tips2; set => SetProperty(ref _tips2, value); }public string Tips3 { get => _tips3; set => SetProperty(ref _tips3, value); }public string Tips4 { get => _tips4; set => SetProperty(ref _tips4, value); }public string Tips5 { get => _tips5; set => SetProperty(ref _tips5, value); }public string Tips6 { get => _tips6; set => SetProperty(ref _tips6, value); }public double ProgressVal { get => _progressVal; set => SetProperty(ref _progressVal, value); }public double ProgressMin { get => _progressMin; set => SetProperty(ref _progressMin, value); }public double ProgressMax { get => _progressMax; set => SetProperty(ref _progressMax, value); }private void OnMutiDownloadStatistics(object sender, GeneralUpdate.Core.Update.MutiDownloadStatisticsEventArgs e){Tips1 = $" { e.Speed } , { e.Remaining.ToShortTimeString() }";}private void OnMutiDownloadProgressChanged(object sender, GeneralUpdate.Core.Update.MutiDownloadProgressChangedEventArgs e){switch (e.Type){case ProgressType.Check:break;case ProgressType.Donwload:ProgressVal = e.BytesReceived;if (ProgressMax != e.TotalBytesToReceive){ProgressMax = e.TotalBytesToReceive;}Tips2 = $" { Math.Round(e.ProgressValue * 100, 2) }% , Receivedbyte:{ e.BytesReceived }M ,Totalbyte:{ e.TotalBytesToReceive }M";break;case ProgressType.Updatefile:break;case ProgressType.Done:break;case ProgressType.Fail:break;default:break;}}private void OnMutiDownloadCompleted(object sender, GeneralUpdate.Core.Update.MutiDownloadCompletedEventArgs e){//Tips3 = $"{ e.Version.Name } download completed.";}private void OnMutiAllDownloadCompleted(object sender, GeneralUpdate.Core.Update.MutiAllDownloadCompletedEventArgs e){if (e.IsAllDownloadCompleted){Tips4 = "AllDownloadCompleted";}else{//foreach (var version in e.FailedVersions)//{// ? Debug.Write($"{ version.Item1.Name }");//}}}private void OnMutiDownloadError(object sender, GeneralUpdate.Core.Update.MutiDownloadErrorEventArgs e){//Tips5 = $"{ e.Version.Name },{ e.Exception.Message }.";}private void OnException(object sender, GeneralUpdate.Core.Update.ExceptionEventArgs e){Tips6 = $"{ e.Exception.Message }";}}到這里為止,upgrade的應用分享已完成。
3.1 Server的應用
這里使用新推出的Minimal api演示,其他的api的模板也同樣適用。
創建完成之后項目結構如下:
這個時候我們再安裝nuget。
安裝完成之后的目錄。
接下來我們再寫代碼。
3.2 Server的應用(非必要)
這里分享的是最新版本推送的功能,基于singal R來實現的。需要對singal r有一定了解。代碼如下:
using GeneralUpdate.AspNetCore.Hubs; using GeneralUpdate.AspNetCore.Services; using GeneralUpdate.Core.DTOs;var builder = WebApplication.CreateBuilder(args); builder.Services.AddSingleton<IUpdateService, GeneralUpdateService>(); builder.Services.AddSignalR(); var app = builder.Build();app.MapHub<VersionHub>("/versionhub");app.Use(async (context, next) => {var hubContext = context.RequestServices.GetRequiredService<IHubContext<VersionHub>>();await CommonHubContextMethod((IHubContext)hubContext);if (next != null){await next.Invoke();} });async Task CommonHubContextMethod(IHubContext context) {await context.Clients.All.SendAsync("clientMethod", ""); }到這里為止,server的應用分享已完成。
開源不易希望大家能多多支持。可能或多或少會有些bug希望大家多多反饋,感謝各位的支持。
關鍵詞:C/S、WPF、MAUI、Winfrom、Avalonia、Console App、UWP、WinUI、Linux、Windows、MacOS、自動更新、自動升級、更新、推送。
總結
以上是生活随笔為你收集整理的如何使用GeneralUpdte构建客户端自动升级功能的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CondenserDotNet - 使用
- 下一篇: Xamarin效果第十八篇之GIS中复合