实现一个简易的自动升级工具
在保持某XX系统不做改动的情况下,加入自动更行功能。
考虑使用外部程序作为启动器,先检测版本执行更新,再去launch某XX系统的主程序。并且要支持某XX系统主动运行该自动更新工具也可以完成更新。
简单介绍一下自动升级要做的事:
注:适用于支持部分文件升级的系统。
1.服务端提供文件下载服务,提供加密后的自动更新文件清单内容。
2.客户端获取自动更新文件清单,并解密内容,比对主版本,比对文件MD5,关闭主程序,下载需要的文件,覆盖文件到最新版本,重启主程序。
服务端清单文件配置工具,生成清单文件:
清单文件部分内容经过DES加密再存储。
/// <summary>
/// 清单文件 读取并解析XML到对象(C#客户端解析XML可参照此处)
/// </summary>
/// <param name="modelData"></param>
/// <param name="filePath"></param>
private void ReadAutoUpdateMapsXml(UptBaseModel modelData, string filePath)
{
if (!File.Exists(filePath)) return;
var tempModel = XmlHelper.Deserialize<UptBaseModel>(File.ReadAllText(filePath));
if (tempModel != null)
{
modelData.version = tempModel.version;
modelData.description = tempModel.description;
modelData.server = EncryptParse.DESDecrypt(tempModel.server ?? "", EncryptParse.GenerateDESKey(tempModel.version ?? ""));
if (tempModel.files != null && tempModel.files.Count > 0)
{
modelData.count = tempModel.files.Count;
foreach (var item in tempModel.files)
{
//DES解密信息
item.fileName = EncryptParse.DESDecrypt(item.fileName ?? "", EncryptParse.GenerateDESKey(item.hash1));
item.filePath = EncryptParse.DESDecrypt(item.filePath ?? "", EncryptParse.GenerateDESKey(item.hash1));
item.fileDrect = EncryptParse.DESDecrypt(item.fileDrect ?? "", EncryptParse.GenerateDESKey(item.hash1));
item.urlDrect = EncryptParse.DESDecrypt(item.urlDrect ?? "", EncryptParse.GenerateDESKey(item.hash1));
}
modelData.files.Clear();
modelData.files.AddRange(tempModel.files);
}
}
else
{
modelData.version = "1.0.0.0";
modelData.description = "";
modelData.files.Clear();
}
}
/// <summary>
/// 清单文件 加密并保存内容到XML
/// </summary>
/// <param name="modelData"></param>
/// <param name="toPath"></param>
private void SaveAutoUpdateMapsXml(UptBaseModel modelData, string toPath)
{
var tempModel = new UptBaseModel();
tempModel.version = modelData.version;
tempModel.description = modelData.description;
tempModel.server = EncryptParse.DESEncrypt(modelData.server ?? "", EncryptParse.GenerateDESKey(modelData.version ?? ""));
foreach (var item in modelData.files)
{
var info = new FileInfoModel();
info.hash1 = item.hash1;
info.fileVer = item.fileVer;
info.fileBytes = item.fileBytes;
info.lastWTime = item.lastWTime;
//DES加密信息
info.fileName = EncryptParse.DESEncrypt(item.fileName ?? "", EncryptParse.GenerateDESKey(item.hash1));
info.filePath = EncryptParse.DESEncrypt(item.filePath ?? "", EncryptParse.GenerateDESKey(item.hash1));
info.fileDrect = EncryptParse.DESEncrypt(item.fileDrect ?? "", EncryptParse.GenerateDESKey(item.hash1));
info.urlDrect = EncryptParse.DESEncrypt(item.urlDrect ?? "", EncryptParse.GenerateDESKey(item.hash1));
tempModel.files.Add(info);
}
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(XmlHelper.Serialize(tempModel, true, true));
xmlDoc.Save(toPath);
}
客户端自动更新程序,获取清单内容、下载文件:
这里直接使用http下载清单文件。
下载步骤:获取清单文件,通过DES解密内容,获得最新文件下载链接及目标路径,再比对MD5并下载到临时文件夹。
更新步骤:关闭待更新应用主程序,覆盖临时文件夹中的文件到应用程序目录,删除临时文件夹,重启应用主程序。
/// <summary>
/// 获取清单文件链接地址
/// </summary>
/// <param name="serverUrl">文件父目录</param>
/// <param name="arrgs">命令参数</param>
/// <returns></returns>
public static string GetAutoUpdateMapsXmlServerUrl(string serverUrl, string[] arrgs)
{
serverUrl = Common.ServerUrl;
var fileUrl = serverUrl + "/" + _AutoUpdateMapsXmlName;
if (arrgs != null && arrgs.Length > 0)
{
//var url = _arrgs.Where(x => x.ToLower().StartsWith("http:")).FirstOrDefault();
var url = string.Empty;
foreach (var v in arrgs)
{
if (v.ToLower().StartsWith("http:", StringComparison.OrdinalIgnoreCase)) url = v;
if (v.ToLower().StartsWith("https:", StringComparison.OrdinalIgnoreCase)) url = v;
}
if (string.IsNullOrEmpty(url) == false)
{
serverUrl = url;
fileUrl = serverUrl + "/" + _AutoUpdateMapsXmlName;
}
if (url?.ToLower().LastIndexOf(".xml") > 0)
{
fileUrl = url;
}
}
return new Uri(fileUrl).ToString();
}
///<summary>
/// HTTP读取文件文本内容
/// </summary>
/// <param name="fileUrl">文件链接地址</param>
public static string DownloadAutoUpdateMapsXmlText(string fileUrl)
{
HttpWebRequest resq = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(fileUrl);
HttpWebResponse resp = (System.Net.HttpWebResponse)resq.GetResponse();
//判断服务器未响应
if (resp.StatusCode != HttpStatusCode.OK)
{
throw new Exception("文件内容地址响应失败");
}
Stream st = resp.GetResponseStream();
Encoding encode = System.Text.Encoding.GetEncoding("utf-8");
StreamReader reader = new StreamReader(st, encode);
return reader.ReadToEnd();
}
/// <summary>
/// 读取并解析XML到对象
/// </summary>
/// <param name="targetXml"></param>
public static UptBaseModel ReadAutoUpdateMapsXml(string targetXml)
{
UptBaseModel modelData = new UptBaseModel();
if (string.IsNullOrEmpty(targetXml)) return modelData;
var tempModel = XmlHelper.Deserialize<UptBaseModel>(targetXml);
if (tempModel != null)
{
modelData.version = tempModel.version;
modelData.description = tempModel.description;
//modelData.server = EncryptParse.DESDecrypt(tempModel.server ?? "", EncryptParse.GenerateDESKey(tempModel.version ?? ""));
if (tempModel.files != null && tempModel.files.Count > 0)
{
modelData.count = tempModel.files.Count;
foreach (var item in tempModel.files)
{
//DES解密信息
item.fileName = EncryptParse.DESDecrypt(item.fileName, EncryptParse.GenerateDESKey(item.hash1));
item.filePath = EncryptParse.DESDecrypt(item.filePath, EncryptParse.GenerateDESKey(item.hash1));
item.fileDrect = EncryptParse.DESDecrypt(item.fileDrect, EncryptParse.GenerateDESKey(item.hash1));
item.urlDrect = EncryptParse.DESDecrypt(item.urlDrect ?? "", EncryptParse.GenerateDESKey(item.hash1));
}
modelData.files.Clear();
modelData.files.AddRange(tempModel.files);
}
}
else
{
modelData.version = "1.0.0.0";
modelData.description = "";
modelData.files.Clear();
}
return modelData;
}
/// <summary>
/// 创建下载任务
/// </summary>
/// <param name="modelBase"></param>
/// <param name="tempPath"></param>
/// <param name="downloadProgressChangedEventHandler"></param>
/// <param name="asyncCompletedEventHandler"></param>
public static int CreatDownloadTasks(UptBaseModel modelBase, string tempPath, DownloadProgressChangedEventHandler downloadProgressChangedEventHandler = null, AsyncCompletedEventHandler asyncCompletedEventHandler = null)
{
if (modelBase != null && modelBase.files != null && modelBase.files.Count > 0)
{
if (string.IsNullOrEmpty(tempPath)) tempPath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "patch"); //默认文件缓存路径
//缓存目录文件夹不存在则创建
if (Directory.Exists(tempPath) == false)
{
Directory.CreateDirectory(tempPath);
}
foreach (var item in modelBase.files)
{
var toPath = Path.Combine(tempPath, item.fileDrect);
//目标文件夹不存在则创建
if (Directory.Exists(Path.GetDirectoryName(toPath)) == false)
{
Directory.CreateDirectory(Path.GetDirectoryName(toPath));
}
//比对文件MD5码
if (File.Exists(toPath))
{
var nowHash = EncryptParse.GetFileMD5Hash(toPath);
if (nowHash == item.hash1) continue;
}
item.targetPath = toPath;
//开始创建连接下载
WebClient client = new WebClient();
if (downloadProgressChangedEventHandler != null) client.DownloadProgressChanged += downloadProgressChangedEventHandler;
if (asyncCompletedEventHandler != null) client.DownloadFileCompleted += asyncCompletedEventHandler;
client.DownloadFileAsync(new Uri(item.urlDrect), toPath, item);
modelBase.updateCount++;
}
}
return modelBase.updateCount;
}
效果图:
清单配置工具:
自动更新程序:
源代码奉上: