核心代码类设计
文件单元
我们将版本的每一个文件视为一个文件单元,需要获取其相对路径、大小、版本、写操作时间、MD5码等信息。结构定义(含方法):
/// <summary>
/// 文件信息
/// </summary>
public class FileUnit : IComparable<FileUnit>
{
/// <summary>
///
/// </summary>
public FileUnit() { }
/// <summary>
///
/// </summary>
/// <param name="sDirectory">此版本的根目录,如"D:\\MyApp\\MyApp_Build_201906117"</param>
/// <param name="sFullPath">此文件的全路径如"D:\\MyApp\\MyApp_Build_201906117\\Main.exe"</param>
public FileUnit(string sDirectory, string sFullPath)
{
RelativePath = sFullPath.Substring(sDirectory.Length).TrimStart('\\');
Version = GetFileVersion(sFullPath);
var fileInfo = new FileInfo(sFullPath);
FileSize = (int)(fileInfo.Length / 1024);
LastUpgradeTime = fileInfo.LastWriteTime;
Md5Code = GlobalHelper.GetMD5FromFile(sFullPath);
}
/// <summary>
///
/// </summary>
/// <param name="sRelativePath">文件的相对路径,如"Main.exe"</param>
/// <param name="sVersion">版本号</param>
/// <param name="lSize">文件大小(单位:KB)</param>
/// <param name="dtUpdateTime">更新时间(文件的最后写入时间)</param>
public FileUnit(string sRelativePath, string sVersion, long lSize, DateTime dtUpdateTime)
{
RelativePath = sRelativePath;
Version = sVersion;
FileSize = lSize;
LastUpgradeTime = dtUpdateTime;
}
/// <summary>
/// 文件的相对路径
/// </summary>
[JsonProperty("path")]
public string RelativePath { get; set; }
/// <summary>
/// 文件大小(单位:KB)
/// </summary>
[JsonProperty("size")]
public long FileSize { get; set; } = 0;
/// <summary>
/// 更新时间
/// </summary>
[JsonConverter(typeof(CustomizedDateTimeConverter))]
[JsonProperty("time")]
public DateTime LastUpgradeTime { get; set; } = DateTime.Now;
/// <summary>
/// 文件版本
/// </summary>
[JsonProperty("version")]
public string Version { get; set; }
/// <summary>
/// 文件MD5码
/// </summary>
[JsonProperty("md5")]
public string Md5Code { get; set; }
/// <summary>
/// 获取文件的版本信息
/// </summary>
/// <param name="sFileName">文件的完整路径</param>
/// <returns></returns>
public static FileVersionInfo GetFileVersionInfo(string sFileName) => FileVersionInfo.GetVersionInfo(sFileName);
/// <summary>
/// 获取文件的版本号
/// </summary>
/// <param name="sFileName">文件的完整路径</param>
/// <returns></returns>
public static string GetFileVersion(string sFileName) => GetFileVersionInfo(sFileName)?.FileVersion ?? "0.0.0.0";
/// <summary>
/// 获取最近更新时间
/// </summary>
/// <param name="sFileName">文件的完整路径</param>
/// <returns></returns>
public static DateTime GetLatestTime(string sFileName)
{
var fileInfo = new FileInfo(sFileName);
return fileInfo.LastWriteTime;
}
/// <summary>
/// 版本B是否比版本A新
/// </summary>
/// <param name="VersionA"></param>
/// <param name="VersionB"></param>
/// <returns></returns>
public static bool NewerThanVersionA(FileVersionInfo VersionA, FileVersionInfo VersionB)
{
var bNew = false;
try
{
bNew = NewerThanVersionA(VersionA.ToString(), VersionB.ToString());
}
catch { }
return bNew;
}
/// <summary>
/// 版本B是否比版本A新
/// </summary>
/// <param name="sVersionA"></param>
/// <param name="sVersionB"></param>
/// <returns></returns>
public static bool NewerThanVersionA(string sVersionA, string sVersionB)
{
var bNew = false;
try
{
var lstCurrentVersion = sVersionA.Split('.');
var lstOtherVersion = sVersionB.Split('.');
var nLen = lstCurrentVersion.Length > lstOtherVersion.Length ? lstOtherVersion.Length : lstCurrentVersion.Length;
for (int i = 0; i < nLen; i++)
{
if (!int.TryParse(lstOtherVersion[i], out int nOtherVersion) ||
!int.TryParse(lstCurrentVersion[i], out int nCurrentVersion) ||
nOtherVersion <= nCurrentVersion)
{
continue;
}
bNew = true;
break;
}
if (!bNew && lstOtherVersion.Length - lstCurrentVersion.Length > 0)
{
for (int i = lstCurrentVersion.Length - 1; i < lstOtherVersion.Length; i++)
{
if (!int.TryParse(lstOtherVersion[i], out int nOtherVersion) || nOtherVersion <= 0)
continue;
bNew = true;
break;
}
}
}
catch { }
return bNew;
}
/// <summary>
/// 是否相等(比较文件MD5码)
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
if (!(obj is FileUnit fileUnit) || string.IsNullOrEmpty(fileUnit.Md5Code) || string.IsNullOrEmpty(Md5Code))
{
return false;
}
return fileUnit.Md5Code.Equals(Md5Code);
}
/// <summary>
///
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
public int CompareTo(FileUnit other)
{
return RelativePath.CompareTo(other.RelativePath);
}
/// <summary>
/// 获取文件HashCode
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return RelativePath.GetHashCode();
}
}
升级信息
完整版
/// <summary>
/// 升级信息(完整版)
/// </summary>
public class UpgradeInfo
{
public UpgradeInfo() { }
/// <summary>
/// 配置文件路径
/// </summary>
[JsonIgnore]
public static string ConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "upgrade.json");
private readonly object lockObject = new object();
private string _mainVersion = "";
/// <summary>
/// 此版本的主版本号
/// </summary>
[JsonProperty("version")]
public string MainVersion
{
get
{
if (null != MainVersionInfo)
_mainVersion = MainVersionInfo.FileVersion;
return _mainVersion;
}
set { _mainVersion = value; }
}
/// <summary>
/// 此版本的主版本信息
/// </summary>
[JsonIgnore]
public FileVersionInfo MainVersionInfo { get; set; }
private int totalCount = 0;
/// <summary>
/// 此版本文件总数
/// </summary>
[JsonProperty("count")]
public int TotalCount
{
get
{
if (FileUnits.Count > 0)
totalCount = FileUnits.Count;
return totalCount;
}
set { totalCount = value; }
}
private long totalSize = 0;
/// <summary>
/// 此版本文件总大小(单位:KB)
/// </summary>
[JsonProperty("size")]
public long TotalSize
{
get
{
if (FileUnits.Count > 0)
{
totalSize = 0;
foreach (var item in FileUnits)
{
totalSize += item.FileSize;
}
}
return totalSize;
}
set { totalSize = value; }
}
private DateTime lastUpgradeTime = DateTime.MinValue;
/// <summary>
/// 最后更新时间
/// </summary>
[JsonProperty("time")]
[JsonConverter(typeof(CustomizedDateTimeConverter))]
public DateTime LastUpgradeTime
{
get
{
if (0 < FileUnits.Count)
{
var dateTime = DateTime.MinValue;
foreach (var item in FileUnits)
{
if (item.LastUpgradeTime > dateTime)
dateTime = item.LastUpgradeTime;
}
lastUpgradeTime = dateTime;
}
return lastUpgradeTime;
}
set { lastUpgradeTime = value; }
}
/// <summary>
/// 此版本的文件列表
/// </summary>
[JsonProperty("files")]
public IList<FileUnit> FileUnits { get; set; } = new List<FileUnit>();
/// <summary>
/// 保存配置信息
/// </summary>
/// <param name="sConfigurationPath">配置文件路径(为空时取默认路径)</param>
public void Save(string sConfigurationPath = null)
{
try
{
lock (lockObject)
{
if (!string.IsNullOrEmpty(sConfigurationPath) && File.Exists(sConfigurationPath))
ConfigurationPath = sConfigurationPath;
var fileStream = File.Open(ConfigurationPath, FileMode.Create, FileAccess.ReadWrite);
var sValue = JsonConvert.SerializeObject(this);
var byteContent = System.Text.Encoding.Default.GetBytes(sValue);
fileStream.Write(byteContent, 0, byteContent.Length);
fileStream.Flush();
fileStream.Close();
}
}
catch { }
}
/// <summary>
/// 加载配置信息
/// </summary>
/// <param name="sConfigurationPath">配置文件路径(为空时取默认路径)</param>
public void Load(string sConfigurationPath = null)
{
try
{
lock (lockObject)
{
if (!string.IsNullOrEmpty(sConfigurationPath) && File.Exists(sConfigurationPath))
ConfigurationPath = sConfigurationPath;
var upgradeInfo = JsonConvert.DeserializeObject<UpgradeInfo>(File.ReadAllText(ConfigurationPath, System.Text.Encoding.Default));
MainVersionInfo = upgradeInfo.MainVersionInfo;
MainVersion = upgradeInfo.MainVersion;
TotalCount = upgradeInfo.TotalCount;
TotalSize = upgradeInfo.TotalSize;
FileUnits = upgradeInfo.FileUnits;
}
}
catch(Exception ex)
{
Log.Loging.Error(ex.Message);
}
}
}
其中,时间格式转换类为:
/// <summary>
/// 时间格式转换
/// </summary>
public class CustomizedDateTimeConverter : DateTimeConverterBase
{
private static readonly IsoDateTimeConverter DtConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" };
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
DtConverter.WriteJson(writer, value, serializer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return DtConverter.ReadJson(reader, objectType, existingValue, serializer);
}
}
仅摘要
/// <summary>
/// 此版本的主版本信息
/// </summary>
[JsonIgnore]
public FileVersionInfo MainVersionInfo { get; set; }
/// <summary>
/// 此版本的主版本号
/// </summary>
[JsonProperty("version")]
public string MainVersion { get; set; }
/// <summary>
/// 此版本文件总数
/// </summary>
[JsonProperty("count")]
public int TotalCount { get; set; }
/// <summary>
/// 此版本文件总大小(单位:字节)
/// </summary>
[JsonProperty("size")]
public long TotalSize { get; set; }
/// <summary>
/// 最后更新时间
/// </summary>
[JsonProperty("time")]
[JsonConverter(typeof(CustomizedDateTimeConverter))]
public DateTime LastUpgradeTime { get; set; }
}
文件对象集合
/// <summary>
/// 升级信息(仅新版本所有文件信息)
/// </summary>
public class UpgradeFilesInfo
{
public UpgradeFilesInfo() { }
public UpgradeFilesInfo(UpgradeInfo upgradeInfo)
{
FileUnits = upgradeInfo.FileUnits;
}
/// <summary>
/// 此版本的文件列表
/// </summary>
[JsonProperty("files")]
public IList<FileUnit> FileUnits { get; set; } = new List<FileUnit>();
}
响应消息
/// <summary>
/// 返回结果中的错误代码。
/// 0表示OK,其他均表示错误。
/// 错误码区码代码段,如:
/// 1-9表示一大类不同小类的错误,
/// 11-19表示一大类不同小类的错误,
/// 21-29表示一大类不同小类的错误……
/// 依此类推。
/// 通用错误(不细分类型)码为较大的数,如999
/// </summary>
public enum ErrorCode
{
/// <summary>
/// 未知错误
/// </summary>
[Description("未知错误")]
Unknown = -1,
/// <summary>
/// 成功
/// </summary>
[Description("成功")]
Ok = 0,
/// <summary>
/// 非法参数
/// </summary>
[Description("非法参数")]
InvalidParam = 1,
/// <summary>
/// 常见错误
/// </summary>
[Description("常见错误")]
CommonError = 999
}
public interface IResponseBody
{
ErrorCode Code { get; set; }
string Description { get; set; }
object Data { get; set; }
}
/// <summary>
/// 返回结果
/// </summary>
public class ResponseInfo : IResponseBody
{
/// <summary>
/// 状态码
/// </summary>
[JsonProperty("code")]
[JsonRequired]
[DefaultValue(ErrorCode.Unknown)]
public ErrorCode Code { get; set; }
private string _description;
/// <summary>
/// 描述信息
/// </summary>
[JsonProperty("desc")]
public string Description
{
get
{
if (string.IsNullOrEmpty(_description))
_description = EnumTypeHelper.GetEnumDescription(Code);
return _description;
}
set
{
_description = value;
}
}
/// <summary>
/// 数据
/// </summary>
[JsonProperty("data", NullValueHandling = NullValueHandling.Include)]
public object Data { get; set; }
}
/// <inheritdoc />
/// <summary>
/// 成功
/// </summary>
public sealed class ResponseOk : ResponseInfo
{
public ResponseOk() { Code = ErrorCode.Ok; }
}
public class ResponseCommonError : ResponseInfo
{
public ResponseCommonError() { Code = ErrorCode.CommonError; }
}
/// <inheritdoc />
/// <summary>
/// 出错
/// </summary>
public sealed class ResponseError : ResponseCommonError
{
public ResponseError(Exception e) { Description = e.Message; }
public ResponseError(string desc) { Description = desc; }
}