using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Net.Http; using System.Text.RegularExpressions; using System.Threading.Tasks; using Update.Utils; namespace Update.Services { public static class Update { public delegate void DeleUpdateProgress(string step, int nowP, int maxP); public delegate void DeleAfterUpdateProgress(bool ok, string error); private static readonly string DEFAULT_DOWNLOAD_PATH = Path.Combine(Environment.CurrentDirectory, "tmp"); private static readonly List IGNORE_PATHS = new List() { "update" }; public struct UpdateInfo { public string MD5 { get; set; } public double Version { get; set; } } public static async Task UpdateApplication(string url, string targetPath, DeleUpdateProgress updateProgress, DeleAfterUpdateProgress afterUpdateProgress, string tmpPath = null) { await Task.Factory.StartNew(async () => { int maxSteps = 7, step = 1; if (string.IsNullOrEmpty(tmpPath)) { tmpPath = DEFAULT_DOWNLOAD_PATH; } string localFile = Path.Combine(tmpPath, Path.GetFileName(url)); string uncompressPath = Path.Combine(tmpPath, "tmp"); void clearCache() { Task.Run(async () => { await ClearCache(uncompressPath, localFile); }); } //await ClearTempPath(uncompressPath); updateProgress("获取更新参数", step++, maxSteps); UpdateInfo info = await GetUpdateInfo(url); if (info.Version < 1 || string.IsNullOrEmpty(info.MD5)) { AfterUpdateProgressEvent(afterUpdateProgress, false, "获取更新参数失败!", clearCache); return; } updateProgress("下载", step++, maxSteps); if (!await Download(url, localFile)) { AfterUpdateProgressEvent(afterUpdateProgress, false, "下载失败!", clearCache); return; } updateProgress("校验", step++, maxSteps); if (!CheckFileMd5(localFile, info.MD5)) { AfterUpdateProgressEvent(afterUpdateProgress, false, "校验失败!", clearCache); return; } updateProgress("解压", step++, maxSteps); if (!await UnCompress(localFile, uncompressPath)) { AfterUpdateProgressEvent(afterUpdateProgress, false, "解压失败!", clearCache); return; } updateProgress("更新", step++, maxSteps); if (!await CopyDirectory(uncompressPath, targetPath)) { updateProgress("更新失败", step, maxSteps); AfterUpdateProgressEvent(afterUpdateProgress, false, "更新失败!", clearCache); return; } updateProgress("清理缓存", step++, maxSteps); if (!await ClearCache(uncompressPath, localFile)) { updateProgress("清理缓存失败", step, maxSteps); AfterUpdateProgressEvent(afterUpdateProgress, true, "更新完成,清理缓存失败!", null); } else { updateProgress("更新完成", step++, maxSteps); AfterUpdateProgressEvent(afterUpdateProgress, true, "更新完成!", null); } }); } private static void AfterUpdateProgressEvent(DeleAfterUpdateProgress afterUpdateProgress, bool res, string error, Action task) { afterUpdateProgress.Invoke(res, error); task?.Invoke(); } private static async Task GetUpdateInfo(string url) { UpdateInfo info = new UpdateInfo(); try { url = Regex.Replace(url, "\\.[^.]{2,5}$", ".txt", RegexOptions.IgnoreCase); HttpResponseMessage response = await Http.Fetch(new Http.Request(url, Http.Method.Get)); if (response.IsSuccessStatusCode) { string txt = await response.Content.ReadAsStringAsync(); info = Utils.Json.Decode(txt); } } catch { } return info; } private static bool CheckFileMd5(string localFileName, string md5) { return FileExtensions.FileMd5(localFileName) == md5; } private static async Task ClearTempPath(string targetPath) { try { await Task.Run(() => { DirectoryInfo di = new DirectoryInfo(targetPath); if (di.Exists) { di.Delete(true); } FileExtensions.CheckDirectory(targetPath, true); }); return true; } catch { return false; } } private static async Task Download(string url, string localFilename) { try { HttpResponseMessage response = await Utils.Http.Fetch(new Utils.Http.Request(url, Utils.Http.Method.Get)); if (response != null && response.IsSuccessStatusCode) { var bytes = await response.Content.ReadAsByteArrayAsync(); FileExtensions.WriteBytes(localFilename, bytes); } return true; } catch { return false; } } private static async Task UnCompress(string localFilename, string targetPath) { bool r = false; try { r = await Task.Run(async () => { bool res = await ClearTempPath(targetPath); if (res) { ZipFile.ExtractToDirectory(localFilename, targetPath); res = true; } return res; }); } catch { } return r; } private static async Task CopyDirectory(string srcPath, string dstPath) { bool copyDirectory(string sp, string dp) { string np; try { DirectoryInfo dir = new DirectoryInfo(sp); FileSystemInfo[] fileinfo = dir.GetFileSystemInfos(); //获取目录下(不包含子目录)的文件和子目录 foreach (FileSystemInfo i in fileinfo) { np = Path.Combine(dp, i.Name); if (i is DirectoryInfo) //判断是否文件夹 { if (!Directory.Exists(np)) { Directory.CreateDirectory(np); //目标目录下不存在此文件夹即创建子文件夹 } if (!IsIgnoreDirectory(i.FullName, srcPath)) { // 判断是否是忽略目录 copyDirectory(i.FullName, np); //递归调用复制子文件夹 } } else { File.Copy(i.FullName, np, true); //不是文件夹即复制文件,true表示可以覆盖同名文件 } } return true; } catch { return false; } } return await Task.Run(() => { return copyDirectory(srcPath, dstPath); }); } private static bool IsIgnoreDirectory(string p, string srcRootPath) { bool res = false; Uri a = new Uri(p), b; foreach (string s in IGNORE_PATHS) { b = new Uri(Path.Combine(srcRootPath, s)); res = a == b; if (res) { break; } } return res; } private static async Task ClearCache(string cachePath, string cacheUpdateFile) { bool res = false; await Task.Run(() => { try { DirectoryInfo di = new DirectoryInfo(cachePath); if (di.Exists) { di.Delete(true); } if (File.Exists(cacheUpdateFile)) { File.Delete(cacheUpdateFile); } res = true; } catch { } }); return res; } } }