不管是ClickOnce发布还是生成单个文件的发布都是全量更新,当引用的nuget包较多的时候,重复上传和下载的内容就比较多,所以需要增量更新。
一、在数据库增加一个表
CREATE TABLE [dbo].[TApp](
[Version] [int] IDENTITY(1,1) NOT NULL,
[Data] [varbinary](max) NOT NULL,
CONSTRAINT [PK_TApp] PRIMARY KEY CLUSTERED
(
[Version] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
就两列,Version列存放自增长版本号,Data列存放更新的数据。
二、发布
/// <summary>
/// 发布
/// </summary>
/// <param name="path">发布的目标地址</param>
/// <returns></returns>
public static void Publish(string path)
{
if (!Directory.Exists(path))
{
MessageBox.Show("目标地址不存在");
return;
}
var pathLast = Path.Combine(path, "Last");//保存最后一次发布的文件的文件夹
if (!Directory.Exists(pathLast))
{
Directory.CreateDirectory(pathLast);
PathCopy(path, pathLast);
MessageBox.Show("没有需要发布的内容");
return;
}
List<KeyValuePair<string, byte[]>> items = new List<KeyValuePair<string, byte[]>>();
foreach (var fullFilename in Directory.GetFiles(path))
{
var filename = Path.GetFileName(fullFilename);
var lastFilename = Path.Combine(pathLast, filename);
var buffer = File.ReadAllBytes(fullFilename);
if (File.Exists(lastFilename))
{
var lastBuffer = File.ReadAllBytes(lastFilename);
if (buffer.SequenceEqual(lastBuffer))
continue;
}
File.WriteAllBytes(lastFilename, buffer);
items.Add(new KeyValuePair<string, byte[]>(filename, buffer));
}
if (items.Count == 0)
{
MessageBox.Show("没有需要发布的内容");
return;
}
#region 压缩一下
var jsonString = JsonSerializer.Serialize(items);
using MemoryStream instream = new MemoryStream(Encoding.UTF8.GetBytes(jsonString));
using MemoryStream outstream = new MemoryStream();
using DeflateStream defstream = new DeflateStream(outstream, CompressionMode.Compress);
instream.CopyTo(defstream);
defstream.Close();
var data = outstream.ToArray();
#endregion
using SqlConnection cn = new SqlConnection(@"Data Source=.\sqlexpress;initial Catalog=DBApp;integrated Security=True");
var cmd = cn.CreateCommand();
cmd.CommandText = "insert into tapp values (@Data)";
cmd.Parameters.AddWithValue("@Data", data);
cn.Open();
cmd.ExecuteNonQuery();
MessageBox.Show("发布成功");
}
/// <summary>
/// 复制文件夹
/// </summary>
/// <param name="source"></param>
/// <param name="destination"></param>
private static void PathCopy(string source, string destination)
{
foreach (var filename in Directory.GetFiles(source))
File.Copy(filename, Path.Combine(destination, Path.GetFileName(filename)), true);
}
三、客户端更新
public static void Update()
{
var currentPath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
var filenameVersion = Path.Combine(currentPath, "version.txt");
int clientVersion = 0;
if (File.Exists(filenameVersion))
clientVersion = int.Parse(File.ReadAllText(filenameVersion));
using SqlConnection cn = new SqlConnection(@"Data Source=.\sqlexpress;initial Catalog=DBApp;integrated Security=True");
cn.Open();
var cmd = cn.CreateCommand();
#region 获取需要更新的内容
List<KeyValuePair<int, byte[]>> kvs = new List<KeyValuePair<int, byte[]>>();
cmd.CommandText = $"select * from tapp where version>{clientVersion}";
var dr = cmd.ExecuteReader();
while (dr.Read())
kvs.Add(new KeyValuePair<int, byte[]>(dr.GetInt32(0), (byte[])dr[1]));
dr.Close();
if (kvs.Count == 0)
{
MessageBox.Show("没有需要更新的内容");
return;
}
#endregion
var updatePath = Path.Combine(currentPath, "U" + DateTime.Now.Ticks);
Directory.CreateDirectory(updatePath);
foreach (var kv in kvs)
{
#region 解压
using MemoryStream ms = new MemoryStream(kv.Value);
using MemoryStream ms2 = new MemoryStream();
using DeflateStream defStram = new DeflateStream(ms, CompressionMode.Decompress);
defStram.CopyTo(ms2);
defStram.Close();
var buffer = ms2.ToArray();
#endregion
var jsonString = Encoding.UTF8.GetString(buffer);
var items = JsonSerializer.Deserialize<List<KeyValuePair<string, byte[]>>>(jsonString);
foreach (var item in items)
File.WriteAllBytes(Path.Combine(updatePath, item.Key), item.Value);
}
File.WriteAllText(filenameVersion, kvs.Max(ii => ii.Key).ToString());//更新客户端版本号
#region 生成并运行bat文件
var batstr = $@"timeout /t 3
taskkill /pid {Process.GetCurrentProcess().Id}
copy {updatePath} {currentPath}
del {updatePath} /Q
rd {updatePath}
{Process.GetCurrentProcess().MainModule.FileName}
";
var filenameBat = Path.Combine(currentPath, "update.bat");
File.WriteAllText(filenameBat, batstr);
App.Current.Shutdown();
Process.Start(filenameBat);
#endregion
}
四、测试一下
添加两个按钮,对应的click事件如下:
private void BtnPublish_Click(object sender, RoutedEventArgs e)
{
Publish(@"D:\TestHot\ConsoleApp1\WpfUpdate\bin\Release\net5.0-windows\publish");
}
private void BtnUpdate_Click(object sender, RoutedEventArgs e)
{
Update();
}
最后就是vs发布、单击发布按钮、单击更新按钮...