翼度科技»论坛 编程开发 .net 查看内容

WPF简单自动更新(升级)程序+服务端

1

主题

1

帖子

3

积分

新手上路

Rank: 1

积分
3
工作逻辑是用户启动主程序,主程序启动更新程序,更新程序立刻检查是否有已经下载好的更新包,如果有则立刻关闭主程序进行更新,如果没有则访问服务器查询更新包,并在后台静默下载,下载完成后等下一次主程序启动时更新
由于只是简单的更新程序,所以没有用数据库,客户端版本号以一个json文件保存,服务端则直接以压缩包的名称作为版本号
那么首先就要有一个服务端,我这里建了一个简单的Asp.Net Core WebApi程序,只有一个获取包列表和一个下载包的方法
Program 中要先添加允许访问的物理路径设置:
  1.             string packagePath = Path.Combine(AppContext.BaseDirectory, "Packages");
  2.             if (!Directory.Exists(packagePath))
  3.                 Directory.CreateDirectory(packagePath);
  4.             app.UseStaticFiles(new StaticFileOptions
  5.             {
  6.                 FileProvider = new PhysicalFileProvider(packagePath),  //添加允许访问的路径
  7.                 RequestPath = "/Packages"
  8.             });
复制代码
控制器方法:
  1.         [HttpGet]
  2.         public IActionResult GetPackages(long version)
  3.         {
  4.             _logger.LogInformation($"用户查询了一次{version}以后的更新包列表");
  5.             string[] files = Directory.GetFiles(_path, "*.zip", SearchOption.TopDirectoryOnly);
  6.             List<long> packages = new List<long>();
  7.             for (int i = 0; i < files.Length; i++)
  8.             {
  9.                 long serverVersion = Convert.ToInt64(Path.GetFileNameWithoutExtension(files[i]));  //懒得用数据库,直接用压缩包的文件名作为版本号,可自行改进
  10.                 if (serverVersion > version)
  11.                     packages.Add(serverVersion);
  12.             }
  13.             packages.Sort();  //排序一下给回客户端,让客户端从旧到新下载(当然也可以返回到客户端后再排序)
  14.             return new JsonResult(packages);
  15.         }
  16.         [HttpGet]
  17.         public IActionResult Download(long version)
  18.         {
  19.             _logger.LogInformation($"一位用户请求下载{version}版本");
  20.             string fileName = Path.Combine("Packages", version + ".zip");  //这里是绝对路径
  21.             string fileFullName = Path.Combine(AppContext.BaseDirectory, fileName);
  22.             if (!System.IO.File.Exists(fileFullName))
  23.                 return NotFound();  //如果客户端申请下载的更新包不存在,返回404
  24.             return Redirect($"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}/{fileName}");  //重定向到文件路径
  25.         }
复制代码
接下来是客户端那边更新程序的部分,由于健壮性判断有点多,所以只贴下载和替换关键部分,其余部分请下载附件查看
下载部分:
  1.         /// <summary>
  2.         /// 查找服务器上比当前版本新的包列表
  3.         /// </summary>
  4.         /// <param name="version">当前版本</param>
  5.         private static async Task<long[]> GetNewPackages(long version)
  6.         {
  7.             var resp = await _client.GetAsync($"{Constant.Config.Host}UpdateService/GetPackages?version={version}");
  8.             if (!resp.IsSuccessStatusCode)
  9.                 throw new HttpRequestException("查找更新失败", null, resp.StatusCode);
  10.             var jsonPackageList = await resp.Content.ReadAsStringAsync();
  11.             long[]? packages = JsonSerializer.Deserialize<long[]>(jsonPackageList);
  12.             if (packages is null)
  13.                 throw new Exception("更新包名称不是数字");
  14.             return packages;
  15.         }
  16.         /// <summary>
  17.         /// 下载指定版本的包
  18.         /// </summary>
  19.         /// <param name="packages">包名列表</param>
  20.         private static async Task DownloadPackage(long[] packages)
  21.         {
  22.             for (int i = 0; i < packages.Length; i++)
  23.             {
  24.                 string packageName = Path.Combine(Constant.DownloadDirect, $"{packages[i]}.zip");
  25.                 var resp = await _client.GetAsync($"{Constant.Config.Host}UpdateService/Download?version={packages[i]}");
  26.                 if (!resp.IsSuccessStatusCode)
  27.                     throw new HttpRequestException("下载更新失败", null, resp.StatusCode);
  28.                 using Stream stream = resp.Content.ReadAsStream();
  29.                 using FileStream fs = new FileStream(packageName, FileMode.OpenOrCreate, FileAccess.Write);
  30.                 stream.CopyTo(fs);
  31.             }
  32.         }
复制代码
替换文件部分:
  1.         /// <summary>
  2.         /// 解压
  3.         /// </summary>
  4.         private async Task UnzipAllPackages()
  5.         {
  6.             _needUpdatePackages.Sort();
  7.             progUpdate.Maximum = _needUpdatePackages.Count;
  8.             for (int i = 0; i < _needUpdatePackages.Count; i++)
  9.             {
  10.                 txbUpdate.Text = $"正在解压,第{i + 1}个,共{_needUpdatePackages.Count}个";
  11.                 progUpdate.Value = i;
  12.                 await Task.Delay(30);
  13.                 ZipFile.ExtractToDirectory(_needUpdatePackages[i], Constant.UnzipDirect, Encoding.GetEncoding("GB2312"), true);
  14.             }
  15.             progUpdate.Value = _needUpdatePackages.Count;
  16.             if (long.TryParse(Path.GetFileNameWithoutExtension(_needUpdatePackages.Last()), out long lVersion))
  17.             {
  18.                 _updateingVersion = lVersion;
  19.             }
  20.             else
  21.             {
  22.                 MessageBox.Show("更新失败,请联系软件供应商。\r\n错误代码:0001", "错误");
  23.                 return;
  24.             }
  25.             await Task.Delay(30);
  26.         }
  27.         /// <summary>
  28.         /// 替换文件
  29.         /// </summary>
  30.         /// <param name="files">文件列表</param>
  31.         private async Task<bool> UpdateFiles(string[] files)
  32.         {
  33.             for (int i = 0; i < files.Length; i++)
  34.             {
  35.                 txbUpdate.Text = $"正在更新文件,第{i + 1}个,共{files.Length}个";
  36.                 progUpdate.Value = i;
  37.                 if (files[i].Contains("自动更新程序"))
  38.                 {
  39.                     continue;  //自己不能被替换,跳过自己,由主程序替换
  40.                 }
  41.                 string relativeFileName = files[i][Constant.UnzipDirect.Length..];
  42.                 if (relativeFileName[0] == '\\')
  43.                     relativeFileName = relativeFileName[1..];
  44.                 string toName = Path.Combine(Environment.CurrentDirectory, relativeFileName);
  45.                 string toDir = Path.GetDirectoryName(toName)!;
  46.                 if (!Directory.Exists(toDir))
  47.                     Directory.CreateDirectory(toDir);
  48.                 try
  49.                 {
  50.                     File.Copy(files[i], toName, true);
  51.                 }
  52.                 catch
  53.                 {
  54.                     MessageBox.Show($"更新失败,{toName.Substring(Environment.CurrentDirectory.Length)}无法被替换,即将回滚更新", "错误");
  55.                     return false;
  56.                 }
  57.                 _coveredFiles.Add(toName);
  58.                 await Task.Delay(30);
  59.             }
  60.             progUpdate.Value = files.Length;
  61.             await Task.Delay(30);
  62.             return true;
  63.         }
复制代码
还有备份和更新失败后根据备份文件回滚的部分,由于和替换代码大量重叠就不贴出来了,附件的项目里面有,可直接编译运行
本文的主要目的也只是自己做个记录,以后有需要的时候可以直接拿过来改,肯定不如其他软件的更新程序这么完善,大佬们看一乐就好
附件:WPF自动更新程序.zip

来源:https://www.cnblogs.com/Alex1911/archive/2023/02/12/17114033.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具