mirror of
https://github.com/BeyondDimension/SteamTools.git
synced 2026-10-22 09:36:09 +08:00
update version s
This commit is contained in:
@@ -78,6 +78,24 @@ namespace System.Application.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 下载更新包失败,请稍后重试 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string DownloadUpdateFail {
|
||||
get {
|
||||
return ResourceManager.GetString("DownloadUpdateFail", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 当前已是最新版本 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string IsExistUpdateFalse {
|
||||
get {
|
||||
return ResourceManager.GetString("IsExistUpdateFalse", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 新版本更新 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -114,5 +132,14 @@ namespace System.Application.Properties {
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 文件校验失败,请打开官网手动更新 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string UpdatePackVerificationFail {
|
||||
get {
|
||||
return ResourceManager.GetString("UpdatePackVerificationFail", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +123,12 @@
|
||||
<data name="Downloading_" xml:space="preserve">
|
||||
<value>正在下载:{0}%</value>
|
||||
</data>
|
||||
<data name="DownloadUpdateFail" xml:space="preserve">
|
||||
<value>下载更新包失败,请稍后重试</value>
|
||||
</data>
|
||||
<data name="IsExistUpdateFalse" xml:space="preserve">
|
||||
<value>当前已是最新版本</value>
|
||||
</data>
|
||||
<data name="NewVersionUpdate" xml:space="preserve">
|
||||
<value>新版本更新</value>
|
||||
</data>
|
||||
@@ -136,4 +142,7 @@
|
||||
<data name="OpenSourceLibraryList" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\..\Resources\OpenSourceLibraryList.mpo;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="UpdatePackVerificationFail" xml:space="preserve">
|
||||
<value>文件校验失败,请打开官网手动更新</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -0,0 +1,66 @@
|
||||
using System.Application.Models;
|
||||
|
||||
namespace System.Application.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用程序更新服务
|
||||
/// </summary>
|
||||
public interface IAppUpdateService
|
||||
{
|
||||
public static IAppUpdateService Instance => DI.Get<IAppUpdateService>();
|
||||
|
||||
public const float MaxProgressValue = 100f;
|
||||
|
||||
/// <summary>
|
||||
/// 当前进度值
|
||||
/// </summary>
|
||||
float CurrentProgressValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 总进度值
|
||||
/// </summary>
|
||||
float TotalProgressValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否支持服务端分发,如果返回 <see langword="false"/> 将不能使用 DownloadAsync 与 OverwriteUpgradeAsync
|
||||
/// <para>对于 iOS 平台,必须使用 App Store 分发</para>
|
||||
/// </summary>
|
||||
bool IsSupportedServerDistribution { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否有更新
|
||||
/// </summary>
|
||||
bool IsExistUpdate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 新版本信息
|
||||
/// </summary>
|
||||
AppVersionDTO? NewVersionInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 检查更新,返回新版本信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
void CheckUpdate();
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
[Obsolete("use IAppUpdateService.Instance", true)]
|
||||
public class AutoUpdateService
|
||||
{
|
||||
[Obsolete("use IAppUpdateService.Instance.CheckAsync", true)]
|
||||
public void CheckUpdate() => throw new NotImplementedException();
|
||||
|
||||
[Obsolete("use IAppUpdateService.Instance.DownloadAsync", true)]
|
||||
public void DownloadUpdate() => throw new NotImplementedException();
|
||||
|
||||
[Obsolete("use IAppUpdateService.Instance.OverwriteUpgradeAsync", true)]
|
||||
public void OverwriteUpgrade(string zipFile) => throw new NotImplementedException();
|
||||
|
||||
[Obsolete("use IAppUpdateService.Instance.NewVersionInfo", true)]
|
||||
public object? UpdateInfo { get; set; }
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Application.Models;
|
||||
using System.Application.Properties;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using static System.Application.Services.IAppUpdateService;
|
||||
|
||||
namespace System.Application.Services.Implementation
|
||||
{
|
||||
public abstract class AppUpdateServiceImpl : IAppUpdateService
|
||||
{
|
||||
protected readonly ICloudServiceClient client;
|
||||
protected readonly AppSettings settings;
|
||||
protected readonly IToast toast;
|
||||
|
||||
public AppUpdateServiceImpl(
|
||||
IToast toast,
|
||||
ICloudServiceClient client,
|
||||
IOptions<AppSettings> options)
|
||||
{
|
||||
this.toast = toast;
|
||||
this.client = client;
|
||||
settings = options.Value;
|
||||
SupportedAbis = GetSupportedAbis();
|
||||
}
|
||||
|
||||
protected virtual ArchitectureFlags GetSupportedAbis()
|
||||
{
|
||||
var architecture = RuntimeInformation.OSArchitecture;
|
||||
return architecture switch
|
||||
{
|
||||
Architecture.Arm => ArchitectureFlags.Arm,
|
||||
Architecture.Arm64 => ArchitectureFlags.Arm64 | ArchitectureFlags.Arm,
|
||||
Architecture.X64 => ArchitectureFlags.X64 | ArchitectureFlags.X86,
|
||||
Architecture.X86 => ArchitectureFlags.X86,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(architecture), architecture, null),
|
||||
};
|
||||
}
|
||||
|
||||
protected ArchitectureFlags SupportedAbis { get; }
|
||||
|
||||
protected abstract Version OSVersion { get; }
|
||||
|
||||
public bool IsSupportedServerDistribution
|
||||
{
|
||||
get
|
||||
{
|
||||
if (DI.Platform == Platform.Apple && DI.DeviceIdiom != DeviceIdiom.Desktop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsExistUpdate { get; protected set; }
|
||||
|
||||
public AppVersionDTO? NewVersionInfo { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当存在新的版本时,重写此方法实现弹窗提示用户
|
||||
/// </summary>
|
||||
protected abstract void OnExistNewVersion();
|
||||
|
||||
static bool isCheckUpdateing;
|
||||
|
||||
public async void CheckUpdate()
|
||||
{
|
||||
if (isCheckUpdateing) return;
|
||||
isCheckUpdateing = true;
|
||||
|
||||
var id = settings.AppVersion;
|
||||
var platform = DI.Platform;
|
||||
var deviceIdiom = DI.DeviceIdiom;
|
||||
if (deviceIdiom == DeviceIdiom.Tablet && platform == Platform.Apple)
|
||||
{
|
||||
deviceIdiom = DeviceIdiom.Phone;
|
||||
}
|
||||
var supportedAbis = SupportedAbis;
|
||||
var osVersion = OSVersion;
|
||||
var rsp = await client.Version.CheckUpdate(id,
|
||||
platform, deviceIdiom,
|
||||
supportedAbis,
|
||||
osVersion);
|
||||
if (rsp.IsSuccess)
|
||||
{
|
||||
if (!rsp.Content.HasValue())
|
||||
{
|
||||
IsExistUpdate = false;
|
||||
toast.Show(SR.IsExistUpdateFalse);
|
||||
}
|
||||
else
|
||||
{
|
||||
IsExistUpdate = true;
|
||||
NewVersionInfo = rsp.Content;
|
||||
OnExistNewVersion();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// ServerApiClient 错误已由全局处理
|
||||
}
|
||||
|
||||
isCheckUpdateing = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 升级包存放文件夹名称
|
||||
/// </summary>
|
||||
const string PackDirName = "-Pack";
|
||||
|
||||
const string FileExDownloadCache = ".download_cache";
|
||||
|
||||
/// <summary>
|
||||
/// 根据新版本信息获取升级包文件名
|
||||
/// </summary>
|
||||
/// <param name="m"></param>
|
||||
/// <returns></returns>
|
||||
static string GetPackFileName(AppVersionDTO m)
|
||||
=> $"{m.Version}@{Hashs.String.Crc32(m.Id.ToStringN())}.{FileEx.TAR_GZ}";
|
||||
|
||||
/// <summary>
|
||||
/// 获取存放升级包缓存文件夹的目录
|
||||
/// </summary>
|
||||
/// <param name="clear">是否需要清理之前的缓存</param>
|
||||
/// <returns></returns>
|
||||
static string GetPackCacheDirPath(bool clear)
|
||||
{
|
||||
var dirPath = Path.Combine(IOPath.CacheDirectory, PackDirName);
|
||||
if (!Directory.Exists(dirPath))
|
||||
{
|
||||
Directory.CreateDirectory(dirPath);
|
||||
}
|
||||
else if (clear)
|
||||
{
|
||||
var files = Directory.GetFiles("*" + FileExDownloadCache);
|
||||
foreach (var item in files)
|
||||
{
|
||||
IOPath.FileTryDelete(item);
|
||||
}
|
||||
}
|
||||
return dirPath;
|
||||
}
|
||||
|
||||
static bool isDownloading;
|
||||
|
||||
/// <summary>
|
||||
/// 是否支持断点续传
|
||||
/// </summary>
|
||||
const bool isSupportedResume = false;
|
||||
|
||||
public float CurrentProgressValue { get; protected set; }
|
||||
|
||||
public float TotalProgressValue { get; protected set; }
|
||||
|
||||
protected void ClearProgressValue(float value = 0f)
|
||||
{
|
||||
CurrentProgressValue = value;
|
||||
TotalProgressValue = value;
|
||||
}
|
||||
|
||||
bool UpdatePackVerification(string filePath, string sha256)
|
||||
{
|
||||
var sha256_ = Hashs.String.SHA256(File.OpenRead(filePath)); // 改为带进度的哈希计算
|
||||
if (TotalProgressValue < MaxProgressValue) TotalProgressValue += 10;
|
||||
return string.Equals(sha256_, sha256, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 下载更新包文件到本地缓存
|
||||
/// </summary>
|
||||
protected async void DownloadUpdate()
|
||||
{
|
||||
if (isDownloading) return;
|
||||
|
||||
if (!IsSupportedServerDistribution) throw new PlatformNotSupportedException();
|
||||
|
||||
isDownloading = true;
|
||||
|
||||
ClearProgressValue();
|
||||
|
||||
var newVersionInfo = NewVersionInfo;
|
||||
if (newVersionInfo.HasValue() && newVersionInfo?.DownloadLink != null && newVersionInfo.SHA256 != null)
|
||||
{
|
||||
var packFileName = GetPackFileName(newVersionInfo);
|
||||
var packFilePath = Path.Combine(GetPackCacheDirPath(!isSupportedResume), packFileName);
|
||||
if (File.Exists(packFilePath)) // 存在安装包文件
|
||||
{
|
||||
if (UpdatePackVerification(packFilePath, newVersionInfo.SHA256)) // (已有文件)哈希验证成功,进行覆盖安装
|
||||
{
|
||||
OverwriteUpgrade(packFilePath);
|
||||
goto end;
|
||||
}
|
||||
else // 验证文件失败,删除源文件,将会重新下载 --提示用户,然后结束本次逻辑
|
||||
{
|
||||
File.Delete(packFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
var cacheFileName = packFileName + FileExDownloadCache;
|
||||
|
||||
CurrentProgressValue = 0;
|
||||
|
||||
var rsp = await client.Download(newVersionInfo.DownloadLink, cacheFileName, new Progress<float>(OnReport));
|
||||
|
||||
void OnReport(float value) => CurrentProgressValue = value;
|
||||
|
||||
if (rsp.IsSuccess)
|
||||
{
|
||||
File.Move(cacheFileName, packFileName);
|
||||
|
||||
if (UpdatePackVerification(packFilePath, newVersionInfo.SHA256)) // (下载文件)哈希验证成功,进行覆盖安装
|
||||
{
|
||||
OverwriteUpgrade(packFileName);
|
||||
OnReport(MaxProgressValue);
|
||||
if (TotalProgressValue < 50) TotalProgressValue = 50;
|
||||
}
|
||||
else
|
||||
{
|
||||
toast.Show(SR.UpdatePackVerificationFail);
|
||||
ClearProgressValue(MaxProgressValue);
|
||||
}
|
||||
}
|
||||
else // 下载失败,进度条填满,可能服务器崩了
|
||||
{
|
||||
toast.Show(SR.DownloadUpdateFail);
|
||||
ClearProgressValue(MaxProgressValue);
|
||||
}
|
||||
}
|
||||
|
||||
end: isDownloading = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 覆盖更新,根据更新包解压覆盖文件
|
||||
/// <para>Android:打开apk包即可</para>
|
||||
/// </summary>
|
||||
protected abstract void OverwriteUpgrade(string fileName);
|
||||
|
||||
/// <summary>
|
||||
/// 在应用商店中打开
|
||||
/// </summary>
|
||||
protected abstract void OpenInAppStore();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user