這一篇就著重寫一下客戶端的代碼,客戶端主要實(shí)現(xiàn)的有:啟動后檢測本地的xml文件,然后發(fā)送到服務(wù)器獲取需要更新的文件以及版本列表。循環(huán)下載。下載成功后,備份原始文件->復(fù)制到主目錄(若失敗進(jìn)行回滾)->修改本地xml文件,更新完成后打開主程序。
開發(fā)環(huán)境:.NET Core 3.1
開發(fā)工具: Visual Studio 2019
實(shí)現(xiàn)代碼:
private void CopyRun() {
string runPath = Process.GetCurrentProcess().MainModule.FileName;
string runName = Path.GetFileName(runPath);
if(!runName.StartsWith("_")) {
string copyPath = runPath.Replace(runName, "_" + runName);
byte[] bytes;
using(FileStream fileStream = new FileStream(runPath, FileMode.Open, FileAccess.Read)) {
bytes = new byte[fileStream.Length];
fileStream.Read(bytes, 0, bytes.Length);
}
using(FileStream fileStream = new FileStream(copyPath, FileMode.create, FileAccess.Write)) {
fileStream.Write(bytes);
}
ProcessStartInfo info = new ProcessStartInfo(copyPath, _runApp);
Process.Start(info);
Environment.Exit(0);
}
}
private void updateUI(Action actoin) {
if(this.InvokeRequired) {
this.BeginInvoke(actoin);
}
else {
actoin();
}
}
private string GetupdateUrl() {
XElement xele = XElement.Load(updateXml);
string url = xele.Element("url").Value;
return url;
}
private string GetupdateFiles() {
XDocument xdoc = XDocument.Load(updateXml);
var files = from f in xdoc.Root.Element("files").Elements() select new { name = f.Attribute("name").Value, version = f.Attribute("version").Value };
return JsonConvert.SerializeObject(files);
}
private void updateXml(List<updateModel> list) {
XDocument xdoc = XDocument.Load(updateXml);
foreach(var model in list) {
var ele_files = xdoc.Root.Element("files");
XElement xele = ele_files.Elements().FirstOrDefault(s => s.Attribute("name").Value == model.name);
if(xele != null) {
xele.SetAttributeValue("version", model.version);
}
else {
XElement addXele = new XElement("file");
addXele.SetAttributeValue("name", model.name);
addXele.SetAttributeValue("version", model.version);
ele_files.Add(addXele);
}
}
xdoc.Save(updateXml);
}
readonly string _runApp;
public Form_update(string runApp) {
InitializeComponent();
_runApp = runApp;
}
readonly string updateXml = Application.StartupPath + "updateList.xml";
private void btn_update_Click(object sender, EventArgs e) {
#region 設(shè)置ui
btn_update.Enabled = false;
label_status.Text = "正在更新";
#endregion
#region 初始化路徑
string savePath = Application.StartupPath + "temp_update\\";
string backPath = Application.StartupPath + "temp_back\\";
if(Directory.Exists(savePath)) {
Directory.delete(savePath, true);
}
Directory.createDirectory(savePath);
if(Directory.Exists(backPath)) {
Directory.delete(backPath, true);
}
Directory.createDirectory(backPath);
#endregion
#region 圖標(biāo)旋轉(zhuǎn)
int angle = 0;
Image img = pictureBox1.BackgroundImage;
System.Threading.Timer timer = new System.Threading.Timer(s => {
updateUI(() => {
angle = angle == 360 ? 0 : angle + 10;
pictureBox1.BackgroundImage = img.RotateImage(angle);
});
}, null, 0, 100);
#endregion
#region 下載更新
string url = GetupdateUrl();
bool isSuccess = false;
Task.Run(() => {
try {
HttpResult httpResult = HttpUtil.HttpRequest(new HttpItem(url + "GetupdateFiles", requestData: GetupdateFiles()));
if(httpResult.Status) {
updateModel_Out output = JsonConvert.DeserializeObject<updateModel_Out>(httpResult.HttpStringData);
if(output.updateList.Count == 0) {
throw new Exception("當(dāng)前已是最新版本");
}
updateUI(() => {
progressBar1.Maximum = output.updateList.Count + 1;
});
for(int i = 0; i < output.updateList.Count; i++) {
updateModel updateModel = output.updateList[i];
#region 進(jìn)度條
updateUI(() => {
label_status.Text = $"正在更新第 {i + 1}/{output.updateList.Count} 個文件,文件名:{updateModel.name}";
progressBar1.Value = i + 1;
});
#endregion
#region 下載文件
httpResult = HttpUtil.HttpRequest(new HttpItem(url + "DownloadFile", requestData: JsonConvert.SerializeObject(updateModel)));
if(httpResult.Status) {
using(FileStream fileStream = new FileStream(savePath + updateModel.name, FileMode.create)) {
fileStream.Write(httpResult.HttpByteData, 0, httpResult.HttpByteData.Length);
}
}
else {
throw new Exception(updateModel.name + "下載失敗,請重試");
}
#endregion
Task.Delay(1000).Wait();
}
#region 備份
updateUI(() => {
label_status.Text = "正在備份";
});
try {
File.Copy(updateXml, backPath + "updateList.xml");
foreach(var file in output.updateList) {
if(File.Exists(Application.StartupPath + file.name)) {
File.Copy(Application.StartupPath + file.name, backPath + file.name, true);
}
}
}
catch { }
#endregion
#region 完成更新
updateUI(() => {
label_status.Text = "正在完成更新";
progressBar1.Value = progressBar1.Maximum;
});
string[] files = Directory.GetFiles(savePath);
try {
#region 更新成功
foreach(string file in files) {
File.Copy(file, Application.StartupPath + Path.GetFileName(file), true);
}
Directory.delete(savePath, true);
#region 保存最新版本
updateXml(output.updateList);
isSuccess = true;
updateUI(() => {
label_status.Text = "更新完成";
});
#endregion
#endregion
}
catch {
#region 失敗回滾
updateUI(() => {
label_status.Text = "更新失敗,正在回滾";
});
string[] files_back = Directory.GetFiles(backPath);
foreach(string file in files_back) {
File.Copy(file, Application.StartupPath + Path.GetFileName(file), true);
}
File.Copy(backPath + "updateList.xml", updateXml, true);
updateUI(() => {
label_status.Text = "回滾完成";
});
return;
#endregion
}
#endregion
}
else {
throw new Exception("獲取更新列表失敗");
}
}
catch(Exception ex) {
updateUI(() => {
label_status.Text = "更新失?。? + ex.Message;
btn_update.Enabled = true;
});
}
finally {
updateUI(() => {
timer.Change(-1, -1);
pictureBox1.BackgroundImage = img;
if(isSuccess) {
if(File.Exists(_runApp)) {
if(MessageBox.Show("更新完成,是否打開程序", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) {
Process.Start(_runApp);
}
Environment.Exit(0);
}
}
});
}
});
#endregion
}
private void btn_close_Click(object sender, EventArgs e) {
Close();
}
private void panel1_Paint(object sender, PaintEventArgs e) {
Pen pen = new Pen(Color.LightGray);
e.Graphics.DrawLine(pen, new Point(36, 36), new Point(panel1.Width - 36, 36));
}
實(shí)現(xiàn)效果:
代碼解析:主要注釋已經(jīng)在代碼中標(biāo)注了,由于基本都是在線程中進(jìn)行操作的,所以在更新UI的時候封裝了updateUI方法,然后就是加了一個定時器實(shí)現(xiàn)圖標(biāo)的旋轉(zhuǎn)。關(guān)于CopyRun(更新自己)方法,就是用來更新自動更新程序本身的,即運(yùn)行之前復(fù)制一份本身,然后啟動復(fù)制的程序,否則本身正在運(yùn)行的時候是不能更新自己的。
該文章在 2023/2/27 10:17:27 編輯過