由于现在做的系统是基于Winform的,在服务器上需要发布,挺麻烦的,而且改动一点点就得全部上传,忍耐了很久终于做了一个一键发布的工具。原理跟目录同步差不多,只不过要做一些特殊处理。先获取本地的最大版本号,再请求服务器更改,服务器把上面最新的版本复制一份到新版本文件夹(目的是保留老版本)。然后再跟目录同步工具一样,但要排除之前的版本文件夹。
同样,需要一个服务端ashx文件配合
using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Linq;
using System.Xml.Serialization;
namespace WebUpdate
{
/// /// AppPublish 的摘要说明
/// public class AppPublish : IHttpHandler
{
private HttpRequest Request;
private HttpResponse Response;
public void ProcessRequest(HttpContext context)
{
Request = context.Request;
Response = context.Response;
context.Response.ContentType = "text/plain";
try
{
if (!String.IsNullOrEmpty(Request["remotePath"]))
{
//复制
if (Request["action"] == "setmaxversion")
{
string dir = Path.GetDirectoryName(context.Server.UrlDecode(Request["remotePath"]));
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
Response.Write("ok");
return;
}
string maxversion = GetMaxVersion(dir);
if (maxversion != null)
{
if (maxversion != Request["maxversion"])
{
//复制为最大版本
FileHelper.Copy(Path.Combine(dir, "Application Files", maxversion), Path.Combine(dir, "Application Files", Request["maxversion"]));
}
}
Response.Write("ok");
}
//列出数据
if (Request["action"] == "dir")
{
string appDir = context.Server.UrlDecode(Request["remotePath"]);
if (!Directory.Exists(appDir))
{
throw new Exception("远程目录不存在");
}
List list = new List();
LoadFile(list, appDir, appDir, false);
string maxversion = GetMaxVersion(appDir);
if (maxversion != null)
{
LoadFile(list, Path.Combine(appDir, "Application Files", maxversion), appDir, true);
}
XmlSerializer sz = new XmlSerializer(list.GetType());
sz.Serialize(Response.OutputStream, list);
}
//上传
if (Request["action"] == "upload")
{
string filePath = context.Server.UrlDecode(Request["remotePath"]);
string dir = Path.GetDirectoryName(filePath);
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
Request.Files[0].SaveAs(filePath);
Response.Write("上传成功" + filePath);
}
}
}
catch (Exception ex)
{
Response.Write(ex.Message);
}
}
public class FileMessage
{
public long Size { get; set; }
public string Sha1 { get; set; }
public string FileName { get; set; }
}
private void LoadFile(List list, string p, string baseDir, bool isLocaChild)
{
foreach (string file in Directory.GetFiles(p))
{
list.Add(GetFileInfo(file, baseDir));
}
if (isLocaChild)
{
foreach (string folder in Directory.GetDirectories(p))
{
LoadFile(list, folder, baseDir, isLocaChild);
}
}
}
private string GetMaxVersion(string dirPath)
{
List list = new List();
if (!Directory.Exists(Path.Combine(dirPath, "Application Files")))
{
Directory.CreateDirectory(Path.Combine(dirPath, "Application Files"));
}
foreach (string dir in Directory.GetDirectories(Path.Combine(dirPath, "Application Files")))
{
list.Add(Path.GetFileName(dir));
}
if (list.Count > 0)
{
return list.Max();
}
return null;
}
/// /// 获取文件信息
/// private FileMessage GetFileInfo(String filePath, string baseDir)
{
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
long lenth = fs.Length;
System.Security.Cryptography.HashAlgorithm algorithm = System.Security.Cryptography.SHA1.Create();
String result = BitConverter.ToString(algorithm.ComputeHash(fs)).Replace("-", "");
fs.Close();
return new FileMessage { Size = lenth, Sha1 = result, FileName = filePath.Replace(baseDir, "").TrimStart('\\').TrimStart('/') };
}
public bool IsReusable
{
get
{
return false;
}
}
}
}xml配置文件
http://leeup.fyj.me/AppPublish.ashx C:\发布\HAK-leemap\ E:\HAK\Setup\
处理逻辑
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
namespace AppPublish
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
public delegate void Action();
public void SafeCall(Control ctrl, Action callback)
{
if (ctrl.InvokeRequired)
ctrl.Invoke(callback);
else
callback();
}
private void MainForm_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
XmlDocument doc = new XmlDocument();
doc.Load(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "publish.config.xml"));
List list = new List();
foreach (XmlNode node in doc.SelectNodes("/publish/app"))
{
list.Add(node.Attributes["name"].InnerText);
}
this.comboBox1.DataSource = list;
}
/// /// 获取本地最大版本
/// /// /// private string GetLocalMaxVersion(string localPath)
{
List list = new List();
foreach (string dir in Directory.GetDirectories(Path.Combine(localPath, "Application Files")))
{
list.Add(Path.GetFileName(dir));
}
if (list.Count > 0)
{
return list.Max();
}
return null;
}
private void btnPublish_Click(object sender, EventArgs e)
{
this.btnPublish.Text = "发布中...";
this.btnPublish.Enabled = false;
XmlDocument doc = new XmlDocument();
doc.Load(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "publish.config.xml"));
XmlNode appnode = doc.SelectSingleNode("/publish/app[@name='" + this.comboBox1.Text + "']");
string publishUrl = appnode.SelectSingleNode("publishUrl").InnerText;
string localFolder = null;
if (appnode.SelectSingleNode("localFolder") != null)
{
localFolder = appnode.SelectSingleNode("localFolder").InnerText;
}
string remoteFolder = null;
if (appnode.SelectSingleNode("remoteFolder") != null)
{
remoteFolder = appnode.SelectSingleNode("remoteFolder").InnerText;
}
XmlNodeList nlist = appnode.SelectNodes("file");
this.progressBar1.Value = 0;
ThreadPool.QueueUserWorkItem((o) =>
{
try
{
this.txtStatus.Text = "正在获取本地信息...";
//本地文件信息
List localList = new List();
LoadFile(localList, localFolder, localFolder, false);
string maxversion = GetLocalMaxVersion(localFolder);
if (maxversion != null)
{
LoadFile(localList, Path.Combine(localFolder, "Application Files", maxversion), localFolder, true);
}
this.txtStatus.Text = "正在请求服务器更改最大版本...";
WebClient wc = new WebClient();
string result = wc.DownloadString(publishUrl + "?remotePath=" + System.Web.HttpUtility.UrlEncode(remoteFolder) + "&action=setmaxversion&maxversion=" + maxversion);
if (result.Trim() != "ok")
{
this.SafeCall(this.listBox1, () =>
{
this.listBox1.Items.Add("请求服务器更改最大版本错误:server返回" + result);
});
this.txtStatus.Text = "同步出错";
return;
}
this.txtStatus.Text = "正在从服务器获取数据...";
XmlSerializer sz = new XmlSerializer(typeof(List));
//服务器文件信息
List serverList = (List)sz.Deserialize(wc.OpenRead(publishUrl + "?remotePath=" + System.Web.HttpUtility.UrlEncode(remoteFolder) + "&action=dir"));
this.txtStatus.Text = "正在处理...";
//需要同步的文件列表
List syncList = new List();
foreach (FileMessage fm in localList)
{
FileMessage srcFm = GetFileMessage(serverList, fm.FileName);
//如果服务器上不存在该文件 则需同步
if (srcFm == null)
{
string url = publishUrl + "?remotePath=" + System.Web.HttpUtility.UrlEncode(Path.Combine(remoteFolder, fm.FileName)) + "&action=upload";
syncList.Add(new UploadMessage
{
UploadUrl = url,
LocalPath = Path.Combine(localFolder, fm.FileName),
RemoteFileName = Path.Combine(remoteFolder, fm.FileName),
Size = fm.Size
});
}
else
{
//如果不等于服务器上的sha1 则需要同步
if (!srcFm.Sha1.Equals(fm.Sha1, StringComparison.CurrentCultureIgnoreCase))
{
string url = publishUrl + "?remotePath=" + System.Web.HttpUtility.UrlEncode(Path.Combine(remoteFolder, fm.FileName)) + "&action=upload";
syncList.Add(new UploadMessage
{
UploadUrl = url,
LocalPath = Path.Combine(localFolder, fm.FileName),
RemoteFileName = Path.Combine(remoteFolder, fm.FileName),
Size = fm.Size
});
}
}
}
foreach (FileMessage fm in serverList)
{
FileMessage destFm = GetFileMessage(localList, fm.FileName);
//如果客户端不存在该文件 则需删除服务器上的
if (destFm == null)
{
IDictionary dic = new Dictionary();
string url = publishUrl + "?remotePath=" + System.Web.HttpUtility.UrlEncode(Path.Combine(remoteFolder, fm.FileName)) + "&action=upload";
syncList.Add(new UploadMessage
{
UploadUrl = url,
RemoteFileName = Path.Combine(remoteFolder, fm.FileName),
IsDelete = true
});
}
}
if (syncList.Count == 0)
{
MessageBox.Show("已经同步好了。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
this.btnPublish.Text = "发布";
this.btnPublish.Enabled = true;
this.txtStatus.Text = "就绪";
return;
}
//计算总同步大小
long total = 0;
foreach (UploadMessage d in syncList)
{
total += d.Size;
}
this.SafeCall(this.progressBar1, () =>
{
this.progressBar1.Maximum = (int)total;
});
foreach (UploadMessage mess in syncList)
{
try
{
if (mess.IsDelete)
{
//不删除服务器根目录
if (mess.RemoteFileName.Contains("Application Files"))
{
this.txtStatus.Text = "正在删除服务器文件" + mess.RemoteFileName;
wc = new WebClient();
string d = wc.DownloadString(mess.UploadUrl);
this.SafeCall(this.listBox1, () =>
{
this.listBox1.Items.Add(d);
});
continue;
}
}
else
{
string localFile = mess.LocalPath;
this.txtStatus.Text = "正在上传" + localFile;
string url = mess.UploadUrl;
//WebClient wb = new WebClient();
//byte[] b = wb.UploadFile(url, localFile);
//string result = System.Text.Encoding.UTF8.GetString(b);
//this.SafeCall(this.listBox1, () =>
//{
// this.listBox1.Items.Add(result);
//});
HttpUploadHelper up = new HttpUploadHelper();
up.UploadStenEvent += up_UploadStenEvent;
up.CompleteEvent += up_CompleteEvent;
up.Upload_Request(url, localFile, mess.RemoteFileName);
}
}
catch (Exception ex)
{
this.SafeCall(this.listBox1, () =>
{
this.listBox1.Items.Add(ex.Message);
});
this.SafeCall(this.txtErrorCount, () =>
{
this.txtErrorCount.Text = (Convert.ToInt32(txtErrorCount.Text) + 1).ToString();
});
}
}
MessageBox.Show("发布完成。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
this.txtStatus.Text = "就绪";
this.btnPublish.Text = "发布";
this.btnPublish.Enabled = true;
}
catch (Exception ex)
{
this.SafeCall(this.listBox1, () =>
{
this.listBox1.Items.Add(ex.Message);
});
this.txtStatus.Text = "同步出错";
this.btnPublish.Text = "发布";
this.btnPublish.Enabled = true;
}
});
}
void up_CompleteEvent(string result)
{
this.SafeCall(this.listBox1, () =>
{
this.listBox1.Items.Add(result);
});
}
/// /// 获取文件信息
/// private FileMessage GetFileInfo(String filePath, string baseDir)
{
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
long lenth = fs.Length;
System.Security.Cryptography.HashAlgorithm algorithm = System.Security.Cryptography.SHA1.Create();
String result = BitConverter.ToString(algorithm.ComputeHash(fs)).Replace("-", "");
fs.Close();
return new FileMessage { Size = lenth, Sha1 = result, FileName = filePath.Replace(baseDir, "") };
}
private void LoadFile(List list, string p, string baseDir, bool isIncludeChild)
{
foreach (string file in Directory.GetFiles(p))
{
if (file.Contains("publish.config.xml"))
{
continue;
}
list.Add(GetFileInfo(file, baseDir));
}
if (isIncludeChild)
{
foreach (string folder in Directory.GetDirectories(p))
{
LoadFile(list, folder, baseDir, isIncludeChild);
}
}
}
private FileMessage GetFileMessage(List list, string fileName)
{
foreach (FileMessage fm in list)
{
if (fm.FileName.Equals(fileName, StringComparison.CurrentCultureIgnoreCase))
{
return fm;
}
}
return null;
}
private void btnClear_Click(object sender, EventArgs e)
{
this.listBox1.Items.Clear();
//HttpUploadHelper up = new HttpUploadHelper();
//up.UploadStenEvent += up_UploadStenEvent;
//up.Upload_Request("http://www.leemap.com:9999/AppPublish.ashx?remotePath=" + System.Web.HttpUtility.UrlEncode("e:\\iTran.CargoPlus.Face.exe"), @"D:\ZUHCargoPlus\iTran.CargoPlus.Face\bin\Debug\iTran.CargoPlus.Face.exe", "iTran.CargoPlus.Face.exe");
}
void up_UploadStenEvent(UploadEvent e)
{
this.SafeCall(this.progressBar1, () =>
{
this.progressBar1.Value = this.progressBar1.Value += e.CurrentSize;
});
this.SafeCall(this.txtSizeStatus, () =>
{
this.txtSizeStatus.Text = this.progressBar1.Value + "/" + this.progressBar1.Maximum;
});
}
protected override void WndProc(ref Message SystemMessage)
{
switch (SystemMessage.Msg)
{
case 0x0112:
if (((int)SystemMessage.WParam) == 61536)
{
// this.Owner.Close();
Application.ExitThread();
}
else
{
base.WndProc(ref SystemMessage);
}
break;
default:
base.WndProc(ref SystemMessage);
break;
}
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
}
}
public class UploadMessage
{
public long Size { get; set; }
public string Sha1 { get; set; }
public string LocalPath { get; set; }
public string UploadUrl { get; set; }
public string RemoteFileName { get; set; }
public bool IsDelete { get; set; }
}
public class FileMessage
{
public long Size { get; set; }
public string Sha1 { get; set; }
private string _fileName;
public string FileName
{
get
{
return (_fileName == null ? null : _fileName.TrimStart('\\').TrimStart('/'));
}
set
{
_fileName = value;
}
}
}
}
珂珂的个人博客 - 一个程序猿的个人网站