首页 > 编程语言 >WPF简单自动更新(升级)程序+服务端

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

时间:2023-02-12 16:34:22浏览次数:60  
标签:files Task string resp version 自动更新 WPF packages 服务端

工作逻辑是用户启动主程序,主程序启动更新程序,更新程序立刻检查是否有已经下载好的更新包,如果有则立刻关闭主程序进行更新,如果没有则访问服务器查询更新包,并在后台静默下载,下载完成后等下一次主程序启动时更新

由于只是简单的更新程序,所以没有用数据库,客户端版本号以一个json文件保存,服务端则直接以压缩包的名称作为版本号

那么首先就要有一个服务端,我这里建了一个简单的Asp.Net Core WebApi程序,只有一个获取包列表和一个下载包的方法

Program 中要先添加允许访问的物理路径设置:

            string packagePath = Path.Combine(AppContext.BaseDirectory, "Packages");
            if (!Directory.Exists(packagePath))
                Directory.CreateDirectory(packagePath);

            app.UseStaticFiles(new StaticFileOptions
            {
                FileProvider = new PhysicalFileProvider(packagePath),  //添加允许访问的路径
                RequestPath = "/Packages"
            });

控制器方法:

        [HttpGet]
        public IActionResult GetPackages(long version)
        {
            _logger.LogInformation($"用户查询了一次{version}以后的更新包列表");

            string[] files = Directory.GetFiles(_path, "*.zip", SearchOption.TopDirectoryOnly);
            List<long> packages = new List<long>();
            for (int i = 0; i < files.Length; i++)
            {
                long serverVersion = Convert.ToInt64(Path.GetFileNameWithoutExtension(files[i]));  //懒得用数据库,直接用压缩包的文件名作为版本号,可自行改进
                if (serverVersion > version)
                    packages.Add(serverVersion);
            }
            packages.Sort();  //排序一下给回客户端,让客户端从旧到新下载(当然也可以返回到客户端后再排序)
            return new JsonResult(packages);
        }

        [HttpGet]
        public IActionResult Download(long version)
        {
            _logger.LogInformation($"一位用户请求下载{version}版本");

            string fileName = Path.Combine("Packages", version + ".zip");  //这里是绝对路径
            string fileFullName = Path.Combine(AppContext.BaseDirectory, fileName);
            if (!System.IO.File.Exists(fileFullName))
                return NotFound();  //如果客户端申请下载的更新包不存在,返回404

            return Redirect($"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}/{fileName}");  //重定向到文件路径
        }

接下来是客户端那边更新程序的部分,由于健壮性判断有点多,所以只贴下载和替换关键部分,其余部分请下载附件查看
下载部分:

        /// <summary>
        /// 查找服务器上比当前版本新的包列表
        /// </summary>
        /// <param name="version">当前版本</param>
        private static async Task<long[]> GetNewPackages(long version)
        {
            var resp = await _client.GetAsync($"{Constant.Config.Host}UpdateService/GetPackages?version={version}");
            if (!resp.IsSuccessStatusCode)
                throw new HttpRequestException("查找更新失败", null, resp.StatusCode);

            var jsonPackageList = await resp.Content.ReadAsStringAsync();
            long[]? packages = JsonSerializer.Deserialize<long[]>(jsonPackageList);
            if (packages is null)
                throw new Exception("更新包名称不是数字");
            return packages;
        }

        /// <summary>
        /// 下载指定版本的包
        /// </summary>
        /// <param name="packages">包名列表</param>
        private static async Task DownloadPackage(long[] packages)
        {
            for (int i = 0; i < packages.Length; i++)
            {
                string packageName = Path.Combine(Constant.DownloadDirect, $"{packages[i]}.zip");
                var resp = await _client.GetAsync($"{Constant.Config.Host}UpdateService/Download?version={packages[i]}");
                if (!resp.IsSuccessStatusCode)
                    throw new HttpRequestException("下载更新失败", null, resp.StatusCode);

                using Stream stream = resp.Content.ReadAsStream();
                using FileStream fs = new FileStream(packageName, FileMode.OpenOrCreate, FileAccess.Write);
                stream.CopyTo(fs);
            }
        }

替换文件部分:

        /// <summary>
        /// 解压
        /// </summary>
        private async Task UnzipAllPackages()
        {
            _needUpdatePackages.Sort();
            progUpdate.Maximum = _needUpdatePackages.Count;
            for (int i = 0; i < _needUpdatePackages.Count; i++)
            {
                txbUpdate.Text = $"正在解压,第{i + 1}个,共{_needUpdatePackages.Count}个";
                progUpdate.Value = i;
                await Task.Delay(30);
                ZipFile.ExtractToDirectory(_needUpdatePackages[i], Constant.UnzipDirect, Encoding.GetEncoding("GB2312"), true);
            }
            progUpdate.Value = _needUpdatePackages.Count;

            if (long.TryParse(Path.GetFileNameWithoutExtension(_needUpdatePackages.Last()), out long lVersion))
            {
                _updateingVersion = lVersion;
            }
            else
            {
                MessageBox.Show("更新失败,请联系软件供应商。\r\n错误代码:0001", "错误");
                return;
            }

            await Task.Delay(30);
        }

        /// <summary>
        /// 替换文件
        /// </summary>
        /// <param name="files">文件列表</param>
        private async Task<bool> UpdateFiles(string[] files)
        {
            for (int i = 0; i < files.Length; i++)
            {
                txbUpdate.Text = $"正在更新文件,第{i + 1}个,共{files.Length}个";
                progUpdate.Value = i;

                if (files[i].Contains("自动更新程序"))
                {
                    continue;  //自己不能被替换,跳过自己,由主程序替换
                }

                string relativeFileName = files[i][Constant.UnzipDirect.Length..];
                if (relativeFileName[0] == '\\')
                    relativeFileName = relativeFileName[1..];
                string toName = Path.Combine(Environment.CurrentDirectory, relativeFileName);
                string toDir = Path.GetDirectoryName(toName)!;
                if (!Directory.Exists(toDir))
                    Directory.CreateDirectory(toDir);
                try
                {
                    File.Copy(files[i], toName, true);
                }
                catch
                {
                    MessageBox.Show($"更新失败,{toName.Substring(Environment.CurrentDirectory.Length)}无法被替换,即将回滚更新", "错误");
                    return false;
                }
                _coveredFiles.Add(toName);
                await Task.Delay(30);
            }
            progUpdate.Value = files.Length;
            await Task.Delay(30);
            return true;
        }

还有备份和更新失败后根据备份文件回滚的部分,由于和替换代码大量重叠就不贴出来了,附件的项目里面有,可直接编译运行

本文的主要目的也只是自己做个记录,以后有需要的时候可以直接拿过来改,肯定不如其他软件的更新程序这么完善,大佬们看一乐就好

附件:WPF自动更新程序.zip

标签:files,Task,string,resp,version,自动更新,WPF,packages,服务端
From: https://www.cnblogs.com/Alex1911/p/17114033.html

相关文章

  • WPF数据绑定机制是如何实现 转载
    转载自https://blog.51cto.com/u_15127553/4275829 接触MVVM模式也有一段时间了,这种将前后台分离开了的设计模式一下子就吸引了我,也是当时一直有一个问题困扰了我很久:WP......
  • 给服务端小白的一些建议
    一、技术看视频教程入门(B站即可),看书深入,最重要的是实践。二、怎么提问很多新人不知道怎么提问,问了太简单的问题怕被人鄙视,更重要的是不知道问题是否简单。其实也没有那......
  • 《植物大战僵尸》 辅助编写5—— 编写wpf游戏辅助
    这里一开始用了Pinvoke.net,后来发现它的WriteProcessMemory接口和win32APi里的WriteProcessMemory不太一样。就抄了一个其他的。internalpartialclassCheatCore......
  • 【WPF】记录一些控件的实现
    1.流动路径可以通过Path来定义路线,虚线从起点流向终点<UserControl.Resources><Storyboardx:Key="OnLoaded1"x:Name="_Storyboard"><DoubleAnimationUs......
  • 界面组件Telerik UI for WPF R1 2023——让导航栏变得更智能!
    TelerikUIforWPF拥有超过100个控件来创建美观、高性能的桌面应用程序,同时还能快速构建企业级办公WPF应用程序。UIforWPF支持MVVM、触摸等,创建的应用程序可靠且结构良......
  • windows 出现自动更新处理方式
    现象:关机或者重启时,开始菜单显示:关机并更新;重启并更新步骤一:关闭windowsupdate服务,并且禁用步骤二:取消更新通知:开始菜单-设置-更新和安全-windows更新-高级选项-关闭更......
  • WPF常用UI库和图表库(MahApps、HandyControl、LiveCharts)
    WPF常用UI库和图标库(MahApps、HandyControl、LiveCharts)WPF有很多开源免费的UI库,本文主要介绍常见的MahApps、HandyControl两个UI库;在开发过程中经常会涉及到图表的开发,本......
  • 从一个 Demo 说起 Zookeeper 服务端源码
    从一个Demo说起Zookeeper服务端源码简介Zookeeper目前是Apache下的开源项目,作为最流行的分布式协调系统之一,我们平时开发中熟知的Dubbo、Kafka、Elastic-Job、Hadoop......
  • WPF使用PATH来画圆
    WPF使用Path来画圆,在WPF中可以使用Path(路径)来画圆,而Path支持两种写法:xaml代码格式、标记格式,这里介绍的是标记格式:例子:<PathData="M300,300A100,1000......
  • 【Windows】Microsoft Store无法打开:关闭自动更新(请联系系统管理员更改此设置)
    ✨MicrosoftStore无法打开/关闭自动更新自动更新应用选项无法更改提示请联系你的系统管理员以更改此设置此现象可能为使用以下命令重新安装MicrosoftStore后出现get......