diff --git a/SteamToolsV2+.sln b/SteamToolsV2+.sln
index 11e39a9f6..7937d97c8 100644
--- a/SteamToolsV2+.sln
+++ b/SteamToolsV2+.sln
@@ -81,6 +81,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Titanium.Web.Proxy", "refer
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Application.SteamTools.Tools.OpenSourceLibraryList", "System.Application.SteamTools.Tools.OpenSourceLibraryList\System.Application.SteamTools.Tools.OpenSourceLibraryList.csproj", "{CF7C2B80-D393-45EA-A67D-DF46FF17B0F8}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3_Test", "3_Test", "{EA0809CE-2CE7-4173-B915-A6BB01522EB5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Application.SteamTools.Client.Test", "System.Application.SteamTools.Client.Test\System.Application.SteamTools.Client.Test.csproj", "{8EDF4429-251A-416D-BB68-93F227191BCF}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -293,6 +297,14 @@ Global
{CF7C2B80-D393-45EA-A67D-DF46FF17B0F8}.Release|Any CPU.Build.0 = Release|Any CPU
{CF7C2B80-D393-45EA-A67D-DF46FF17B0F8}.Release|x86.ActiveCfg = Release|Any CPU
{CF7C2B80-D393-45EA-A67D-DF46FF17B0F8}.Release|x86.Build.0 = Release|Any CPU
+ {8EDF4429-251A-416D-BB68-93F227191BCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8EDF4429-251A-416D-BB68-93F227191BCF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8EDF4429-251A-416D-BB68-93F227191BCF}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {8EDF4429-251A-416D-BB68-93F227191BCF}.Debug|x86.Build.0 = Debug|Any CPU
+ {8EDF4429-251A-416D-BB68-93F227191BCF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8EDF4429-251A-416D-BB68-93F227191BCF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8EDF4429-251A-416D-BB68-93F227191BCF}.Release|x86.ActiveCfg = Release|Any CPU
+ {8EDF4429-251A-416D-BB68-93F227191BCF}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -331,6 +343,8 @@ Global
{3C05CED6-7712-40F0-AF17-8DD3F4C0A224} = {8EB91F53-1062-4E9B-8E4A-096F35EA688D}
{35D60129-6652-4B6E-A04A-9766C3CF1869} = {8EB91F53-1062-4E9B-8E4A-096F35EA688D}
{CF7C2B80-D393-45EA-A67D-DF46FF17B0F8} = {CC6FF77A-6E6B-4D42-B12B-0177745E9D22}
+ {EA0809CE-2CE7-4173-B915-A6BB01522EB5} = {B43459A1-709B-4623-9677-AAC2FEE4FBA3}
+ {8EDF4429-251A-416D-BB68-93F227191BCF} = {EA0809CE-2CE7-4173-B915-A6BB01522EB5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3833E7C5-69BC-4CB3-92CE-A8854AA35D8A}
diff --git a/System.Application.SteamTools.Client.Desktop/ServiceCollectionExtensions.cs b/System.Application.SteamTools.Client.Desktop/ServiceCollectionExtensions.cs
index 7382dcd3f..da38f9fa9 100644
--- a/System.Application.SteamTools.Client.Desktop/ServiceCollectionExtensions.cs
+++ b/System.Application.SteamTools.Client.Desktop/ServiceCollectionExtensions.cs
@@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Logging;
using System.Application.Services;
using System.Application.Services.Implementation;
using System.Application.UI;
@@ -15,7 +16,12 @@ namespace Microsoft.Extensions.DependencyInjection
///
public static IServiceCollection AddDesktopLogging(this IServiceCollection services)
{
- services.AddLogging(AppHelper.Configure);
+ var (minLevel, cfg) = AppHelper.Configure();
+ services.AddLogging(cfg);
+ services.Configure(o =>
+ {
+ o.MinLevel = minLevel;
+ });
return services;
}
@@ -41,18 +47,6 @@ namespace Microsoft.Extensions.DependencyInjection
return services;
}
- ///
- /// 添加 通用 Http 服务
- ///
- ///
- ///
- public static IServiceCollection AddHttpService(this IServiceCollection services)
- {
- services.AddHttpClient(); // 添加 System.Net.Http.HttpClient / HttpClientFactory
- services.AddSingleton();
- return services;
- }
-
///
/// 添加 Steam 相关助手、工具类服务
///
diff --git a/System.Application.SteamTools.Client.Desktop/Services/IHttpService.cs b/System.Application.SteamTools.Client.Desktop/Services/IHttpService.cs
deleted file mode 100644
index 7df4725db..000000000
--- a/System.Application.SteamTools.Client.Desktop/Services/IHttpService.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace System.Application.Services
-{
- public interface IHttpService
- {
- public static IHttpService Instance => DI.Get();
-
- Task GetAsync(string requestUri, CancellationToken cancellationToken = default) where T : notnull;
- }
-
-#if DEBUG
-
- [Obsolete("use IHttpService.Instance", true)]
- public class HttpServices
- {
- }
-
-#endif
-}
\ No newline at end of file
diff --git a/System.Application.SteamTools.Client.Desktop/Services/Implementation/HttpServiceImpl.cs b/System.Application.SteamTools.Client.Desktop/Services/Implementation/HttpServiceImpl.cs
deleted file mode 100644
index 8c919d5f1..000000000
--- a/System.Application.SteamTools.Client.Desktop/Services/Implementation/HttpServiceImpl.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-using Microsoft.Extensions.Logging;
-using Newtonsoft.Json;
-using System.IO;
-using System.Net.Http;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Xml.Serialization;
-
-namespace System.Application.Services.Implementation
-{
- internal sealed class HttpServiceImpl : HttpService, IHttpService
- {
- const string TAG = "HttpS";
- const string Accept = MediaTypeNames.JSON;
- readonly JsonSerializer jsonSerializer = new JsonSerializer();
-
- public HttpServiceImpl(ILoggerFactory loggerFactory, IHttpPlatformHelper http_helper, IHttpClientFactory clientFactory) : base(loggerFactory.CreateLogger(TAG), http_helper, clientFactory)
- {
- }
-
- public async Task GetAsync(string requestUri, CancellationToken cancellationToken) where T : notnull
- {
- HttpRequestMessage? request = null;
- HttpResponseMessage? response = null;
- bool notDispose = false;
- try
- {
- request = new HttpRequestMessage(HttpMethod.Get, requestUri);
- request.Headers.Accept.ParseAdd(Accept);
- request.Headers.UserAgent.ParseAdd(http_helper.UserAgent);
- var client = CreateClient();
- response = await client.SendAsync(request,
- HttpCompletionOption.ResponseHeadersRead,
- cancellationToken)
- .ConfigureAwait(false);
- if (response.IsSuccessStatusCode)
- {
- if (response.Content != null)
- {
- var rspContentClrType = typeof(T);
- if (rspContentClrType == typeof(string))
- {
- return (T)(object)await response.Content.ReadAsStringAsync();
- }
- else if (rspContentClrType == typeof(byte[]))
- {
- return (T)(object)await response.Content.ReadAsByteArrayAsync();
- }
- else if (rspContentClrType == typeof(Stream))
- {
- notDispose = true;
- return (T)(object)await response.Content.ReadAsStreamAsync();
- }
- var mime = response.Content.Headers.ContentType?.MediaType ?? Accept;
- switch (mime)
- {
- case MediaTypeNames.JSON:
- case MediaTypeNames.XML:
- case MediaTypeNames.XML_APP:
- {
- using var stream = await response.Content
- .ReadAsStreamAsync().ConfigureAwait(false);
- using var reader = new StreamReader(stream, Encoding.UTF8);
- switch (mime)
- {
- case MediaTypeNames.JSON:
- {
- using var json = new JsonTextReader(reader);
- return jsonSerializer.Deserialize(json);
- }
- case MediaTypeNames.XML:
- case MediaTypeNames.XML_APP:
- {
- var xmlSerializer = new XmlSerializer(typeof(T));
- return (T)xmlSerializer.Deserialize(reader);
- }
- }
- }
- break;
- }
- }
- }
- }
- catch (Exception e)
- {
- Log.Error(TAG, e, "GetAsync Fail.");
- }
- finally
- {
- if (!notDispose)
- {
- request?.Dispose();
- response?.Dispose();
- }
- }
- return default;
- }
- }
-}
\ No newline at end of file
diff --git a/System.Application.SteamTools.Client.Desktop/UI/AppHelper.cs b/System.Application.SteamTools.Client.Desktop/UI/AppHelper.cs
index be2911e45..96555e9fc 100644
--- a/System.Application.SteamTools.Client.Desktop/UI/AppHelper.cs
+++ b/System.Application.SteamTools.Client.Desktop/UI/AppHelper.cs
@@ -76,15 +76,21 @@ namespace System.Application.UI
///
static LoggerFilterOptions LoggerFilterOptions => DI.Get>().Value;
- internal static void Configure(ILoggingBuilder builder)
+ internal static (LogLevel minLevel, Action cfg) Configure()
{
- var logLevel = DefaultLoggerMinLevel;
- builder.SetMinimumLevel(logLevel);
- SetNLoggerMinLevel(logLevel);
+ var minLevel = DefaultLoggerMinLevel;
- // 可以多个日志提供同时用,比如还可以在 Win 平台再添加一个 Windows 事件日志
+ Action cfg = builder =>
+ {
+ builder.SetMinimumLevel(minLevel);
+ SetNLoggerMinLevel(minLevel);
- builder.AddNLog(); // 添加 NLog 日志
+ // 可以多个日志提供同时用,比如还可以在 Win 平台再添加一个 Windows 事件日志
+
+ builder.AddNLog(); // 添加 NLog 日志
+ };
+
+ return (minLevel, cfg);
}
///
diff --git a/System.Application.SteamTools.Client.Desktop/UI/ViewModels/Pages/SteamAccountPageViewModel.cs b/System.Application.SteamTools.Client.Desktop/UI/ViewModels/Pages/SteamAccountPageViewModel.cs
index 4a2c0953d..ccd529524 100644
--- a/System.Application.SteamTools.Client.Desktop/UI/ViewModels/Pages/SteamAccountPageViewModel.cs
+++ b/System.Application.SteamTools.Client.Desktop/UI/ViewModels/Pages/SteamAccountPageViewModel.cs
@@ -4,7 +4,6 @@ using System.Application.Services;
using System.Application.UI.Resx;
using System.Collections.Generic;
using System.Collections.ObjectModel;
-using System.IO;
using System.Linq;
using System.Threading.Tasks;
@@ -32,11 +31,10 @@ namespace System.Application.UI.ViewModels
set => this.RaiseAndSetIfChanged(ref _steamUsers, value);
}
-
internal async override Task Initialize()
{
SteamUsers = new ObservableCollection(steamService.GetRememberUserList());
- if (SteamUsers == null || SteamUsers.Count < 1)
+ if (!SteamUsers.Any_Nullable())
{
return;
}
@@ -55,9 +53,9 @@ namespace System.Application.UI.ViewModels
users[i].WantsOfflineMode = temp.WantsOfflineMode;
users[i].SkipOfflineModeWarning = temp.SkipOfflineModeWarning;
users[i].OriginVdfString = temp.OriginVdfString;
- users[i].AvatarMediumStream = string.IsNullOrEmpty(users[i].AvatarMedium) ? null : await httpService.GetAsync(users[i].AvatarMedium);
+ users[i].AvatarMediumStream = string.IsNullOrEmpty(users[i].AvatarMedium) ? null : await httpService.GetImageAsync(users[i].AvatarMedium, ImageChannelType.SteamAvatars);
}
SteamUsers = new ObservableCollection(users.OrderByDescending(o => o.RememberPassword).ThenByDescending(o => o.LastLoginTime).ToList());
}
}
-}
+}
\ No newline at end of file
diff --git a/System.Application.SteamTools.Client.Test/HttpTest.cs b/System.Application.SteamTools.Client.Test/HttpTest.cs
new file mode 100644
index 000000000..20030ecd1
--- /dev/null
+++ b/System.Application.SteamTools.Client.Test/HttpTest.cs
@@ -0,0 +1,43 @@
+using NUnit.Framework;
+using System.Application.Services;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.Application
+{
+ [TestFixture]
+ public class HttpTest
+ {
+ [Test]
+ public void GetImage()
+ {
+ const string api_base = "https://localhost:5001/";
+ var images = new[] {
+ "images/icons/company.png",
+ "images/icons/GitHub-Mark-120px-plus.png",
+ "images/icons/icon.png",
+ "images/company.png",
+ "images/icons/company.png",
+ "images/icons/GitHub-Mark-120px-plus.png",
+ "images/icons/logo.png",
+ "images/company.png",
+ "images/icons/icon.png",
+ "images/icons/icon.png",
+ "images/icons/steam.png" };
+
+ var tasks = images.Select(Handle).ToArray();
+ Task.WaitAll(tasks);
+ TestContext.WriteLine("OK");
+
+ static async Task Handle(string requestUri)
+ {
+ var tid = Thread.CurrentThread.ManagedThreadId;
+ var result = await IHttpService.Instance.GetImageAsync(
+ api_base + requestUri, ImageChannelType.SteamAvatars);
+ var tid2 = Thread.CurrentThread.ManagedThreadId;
+ TestContext.WriteLine($"({tid}-{tid2})requestUri: {requestUri}, length: {result?.Length}");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/System.Application.SteamTools.Client.Test/SetupFixture.cs b/System.Application.SteamTools.Client.Test/SetupFixture.cs
new file mode 100644
index 000000000..e5959551d
--- /dev/null
+++ b/System.Application.SteamTools.Client.Test/SetupFixture.cs
@@ -0,0 +1,52 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using NUnit.Framework;
+using System.IO;
+using System.Logging;
+
+namespace System.Application
+{
+ [SetUpFixture]
+ public class SetupFixture
+ {
+ public static bool DIInit { get; set; }
+
+ [OneTimeSetUp]
+ public void OneTimeSetUp()
+ {
+ // TODO: Add code here that is run before
+ // all tests in the assembly are run
+ if (!DIInit)
+ {
+ DI.Init(ConfigureServices);
+
+ var path = AppContext.BaseDirectory;
+ var path1 = Path.Combine(path, "AppData");
+ IOPath.DirCreateByNotExists(path1);
+ var path2 = Path.Combine(path, "Cache");
+ IOPath.DirCreateByNotExists(path2);
+ string GetAppDataDirectory() => path1;
+ string GetCacheDirectory() => path2;
+ IOPath.InitFileSystem(GetAppDataDirectory, GetCacheDirectory);
+ }
+ }
+
+ [OneTimeTearDown]
+ public void OneTimeTearDown()
+ {
+ // TODO: Add code here that is run after
+ // all tests in the assembly have been run
+ }
+
+ static void ConfigureServices(IServiceCollection services)
+ {
+ services.AddLogging(l => l.AddProvider(NUnitLoggerProvider.Instance));
+ services.Configure(o =>
+ {
+ o.MinLevel = LogLevel.Trace;
+ });
+ services.TryAddHttpPlatformHelper();
+ services.AddHttpService();
+ }
+ }
+}
\ No newline at end of file
diff --git a/System.Application.SteamTools.Client.Test/System.Application.SteamTools.Client.Test.csproj b/System.Application.SteamTools.Client.Test/System.Application.SteamTools.Client.Test.csproj
new file mode 100644
index 000000000..b591281b0
--- /dev/null
+++ b/System.Application.SteamTools.Client.Test/System.Application.SteamTools.Client.Test.csproj
@@ -0,0 +1,32 @@
+
+
+
+ net5.0
+ System.Application
+ UnitTest;$(DefineConstants)
+
+
+
+
+ Properties\AssemblyInfo.cs
+
+
+ Logging\NUnitLogger.cs
+
+
+ Logging\NUnitLoggerProvider.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/System.Application.SteamTools.Client/ImageChannelType.cs b/System.Application.SteamTools.Client/ImageChannelType.cs
new file mode 100644
index 000000000..74c8b8100
--- /dev/null
+++ b/System.Application.SteamTools.Client/ImageChannelType.cs
@@ -0,0 +1,40 @@
+using System.Application;
+using System.Application.Services;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.Application
+{
+ ///
+ /// 图片下载渠道类型
+ ///
+ public enum ImageChannelType
+ {
+ ///
+ /// Steam 头像图片
+ ///
+ SteamAvatars,
+
+ ///
+ /// Steam 游戏图片
+ ///
+ SteamGames,
+ }
+}
+
+namespace System
+{
+ public static class ImageChannelTypeEnumExtensions
+ {
+ ///
+ public static Task GetImageAsync(this IHttpService httpService,
+ string requestUri,
+ ImageChannelType channelType,
+ CancellationToken cancellationToken = default)
+ {
+ var channelType_ = channelType.ToString();
+ return httpService.GetImageAsync(requestUri, channelType_, cancellationToken);
+ }
+ }
+}
\ No newline at end of file
diff --git a/System.Application.SteamTools.Client/ServiceCollectionExtensions.cs b/System.Application.SteamTools.Client/ServiceCollectionExtensions.cs
index 0c359b304..b4be2b635 100644
--- a/System.Application.SteamTools.Client/ServiceCollectionExtensions.cs
+++ b/System.Application.SteamTools.Client/ServiceCollectionExtensions.cs
@@ -1,15 +1,24 @@
using Microsoft.Extensions.DependencyInjection.Extensions;
-using System;
using System.Application.Services;
using System.Application.Services.Implementation;
-using System.Collections.Generic;
-using System.Text;
// ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.DependencyInjection
{
public static class IdentityServiceCollectionExtensions
{
+ ///
+ /// 添加 通用 Http 服务
+ ///
+ ///
+ ///
+ public static IServiceCollection AddHttpService(this IServiceCollection services)
+ {
+ services.AddHttpClient(); // 添加 System.Net.Http.HttpClient / HttpClientFactory
+ services.AddSingleton();
+ return services;
+ }
+
public static IServiceCollection TryAddUserManager(this IServiceCollection services)
{
services.TryAddSingleton();
@@ -17,4 +26,4 @@ namespace Microsoft.Extensions.DependencyInjection
return services;
}
}
-}
+}
\ No newline at end of file
diff --git a/System.Application.SteamTools.Client/Services/IHttpService.cs b/System.Application.SteamTools.Client/Services/IHttpService.cs
new file mode 100644
index 000000000..a21c25475
--- /dev/null
+++ b/System.Application.SteamTools.Client/Services/IHttpService.cs
@@ -0,0 +1,44 @@
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.Application.Services
+{
+ public interface IHttpService
+ {
+ public static IHttpService Instance => DI.Get();
+
+ ///
+ /// 通过 Get 请求 API 内容
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task GetAsync(string requestUri,
+ string accept = MediaTypeNames.JSON,
+ CancellationToken cancellationToken = default) where T : notnull;
+
+ ///
+ /// 通过 Get 请求 Image 内容
+ ///
+ ///
+ /// 渠道类型,根据不同的类型建立不同的缓存文件夹
+ ///
+ ///
+ Task GetImageAsync(
+ string requestUri,
+ string channelType,
+ CancellationToken cancellationToken = default);
+ }
+
+#if DEBUG
+
+ [Obsolete("use IHttpService.Instance", true)]
+ public class HttpServices
+ {
+ }
+
+#endif
+}
\ No newline at end of file
diff --git a/System.Application.SteamTools.Client/Services/Implementation/HttpServiceImpl.cs b/System.Application.SteamTools.Client/Services/Implementation/HttpServiceImpl.cs
new file mode 100644
index 000000000..daafc25a3
--- /dev/null
+++ b/System.Application.SteamTools.Client/Services/Implementation/HttpServiceImpl.cs
@@ -0,0 +1,255 @@
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using System.IO;
+using System.IO.FileFormats;
+using System.Net.Http;
+using System.Text;
+using System.Threading;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Xml.Serialization;
+using System.Collections.Concurrent;
+using System.Security.Cryptography;
+
+namespace System.Application.Services.Implementation
+{
+ internal sealed class HttpServiceImpl : GeneralHttpClientFactory, IHttpService
+ {
+ const string TAG = "HttpS";
+ readonly JsonSerializer jsonSerializer = new();
+
+ public HttpServiceImpl(
+ ILoggerFactory loggerFactory,
+ IHttpPlatformHelper http_helper,
+ IHttpClientFactory clientFactory)
+ : base(loggerFactory.CreateLogger(TAG), http_helper, clientFactory)
+ {
+ }
+
+ public async Task GetAsync(
+ string requestUri,
+ string accept,
+ CancellationToken cancellationToken) where T : notnull
+ {
+ HttpRequestMessage? request = null;
+ HttpResponseMessage? response = null;
+ bool notDispose = false;
+ try
+ {
+ request = new HttpRequestMessage(HttpMethod.Get, requestUri);
+ request.Headers.Accept.ParseAdd(accept);
+ request.Headers.UserAgent.ParseAdd(http_helper.UserAgent);
+ var client = CreateClient();
+ response = await client.SendAsync(request,
+ HttpCompletionOption.ResponseHeadersRead,
+ cancellationToken)
+ .ConfigureAwait(false);
+ if (response.IsSuccessStatusCode)
+ {
+ if (response.Content != null)
+ {
+ var rspContentClrType = typeof(T);
+ if (rspContentClrType == typeof(string))
+ {
+ return (T)(object)await response.Content.ReadAsStringAsync();
+ }
+ else if (rspContentClrType == typeof(byte[]))
+ {
+ return (T)(object)await response.Content.ReadAsByteArrayAsync();
+ }
+ else if (rspContentClrType == typeof(Stream))
+ {
+ notDispose = true;
+ return (T)(object)await response.Content.ReadAsStreamAsync();
+ }
+ var mime = response.Content.Headers.ContentType?.MediaType ?? accept;
+ switch (mime)
+ {
+ case MediaTypeNames.JSON:
+ case MediaTypeNames.XML:
+ case MediaTypeNames.XML_APP:
+ {
+ using var stream = await response.Content
+ .ReadAsStreamAsync().ConfigureAwait(false);
+ using var reader = new StreamReader(stream, Encoding.UTF8);
+ switch (mime)
+ {
+ case MediaTypeNames.JSON:
+ {
+ using var json = new JsonTextReader(reader);
+ return jsonSerializer.Deserialize(json);
+ }
+ case MediaTypeNames.XML:
+ case MediaTypeNames.XML_APP:
+ {
+ var xmlSerializer = new XmlSerializer(typeof(T));
+ return (T)xmlSerializer.Deserialize(reader);
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Log.Error(TAG, e, "GetAsync Fail.");
+ }
+ finally
+ {
+ if (!notDispose)
+ {
+ request?.Dispose();
+ response?.Dispose();
+ }
+ }
+ return default;
+ }
+
+ async Task GetImageAsync_(
+ string requestUri,
+ string localCacheFilePath,
+ CancellationToken cancellationToken)
+ {
+ var localCacheFilePathExists = File.Exists(localCacheFilePath);
+
+ try
+ {
+ if (localCacheFilePathExists) // 存在缓存文件
+ {
+ var fileStream = File.OpenRead(localCacheFilePath);
+ if (FileFormat.IsImage(fileStream, out var format))
+ {
+ if (http_helper.SupportedImageFormats.Contains(format)) // 读取缓存并且格式符合要求
+ {
+ fileStream.Close();
+ Log.Debug(TAG, "GetImageAsync localCacheFilePath: {0}", localCacheFilePath);
+ return localCacheFilePath;
+ }
+ else // 格式不准确
+ {
+ Log.Error(TAG, "GetImageAsync Not Supported ImageFormat: {0}.", format);
+ }
+ }
+ else // 未知的图片格式
+ {
+ Log.Error(TAG, "GetImageAsync Unknown ImageFormat.");
+ }
+ fileStream.Close();
+ }
+ }
+ catch (Exception e)
+ {
+ Log.Error(TAG, e, "GetImageAsync Load LocalFile Fail.");
+ }
+
+ if (localCacheFilePathExists)
+ {
+ File.Delete(localCacheFilePath); // 必须删除文件,重新下载覆盖
+ }
+
+ try
+ {
+ Log.Debug(TAG, "GetImageAsync requestUri: {0}", requestUri);
+ //#if DEBUG
+ // var now = DateTime.Now;
+ // var filePath = Path.Combine(IOPath.CacheDirectory, $"{now.ToString(DateTimeFormat.Connect)}-{Hashs.String.Crc32(requestUri)}");
+ // File.WriteAllText(filePath, now.ToString());
+ //#endif
+ var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
+ request.Headers.Accept.ParseAdd(http_helper.AcceptImages);
+ request.Headers.UserAgent.ParseAdd(http_helper.UserAgent);
+ var client = CreateClient();
+ var response = await client.SendAsync(request,
+ HttpCompletionOption.ResponseHeadersRead,
+ cancellationToken)
+ .ConfigureAwait(false);
+ if (response.IsSuccessStatusCode)
+ {
+ var stream = await response.Content.ReadAsStreamAsync();
+ var localCacheFilePath2 = localCacheFilePath + ".download-cache";
+ var fileStream = File.Create(localCacheFilePath2);
+ cancellationToken.Register(() =>
+ {
+ fileStream.Close();
+ IOPath.FileTryDelete(localCacheFilePath2);
+ });
+ await stream.CopyToAsync(fileStream, cancellationToken);
+ await fileStream.FlushAsync();
+ bool isSupportedImageFormat;
+ if (FileFormat.IsImage(fileStream, out var format))
+ {
+ if (http_helper.SupportedImageFormats.Contains(format)) // 读取缓存并且格式符合要求
+ {
+ isSupportedImageFormat = true;
+ }
+ else // 格式不准确
+ {
+ Log.Error(TAG,
+ "GetImageAsync Download Not Supported ImageFormat: {0}.", format);
+ isSupportedImageFormat = false;
+ }
+ }
+ else // 未知的图片格式
+ {
+ Log.Error(TAG, "GetImageAsync Download Unknown ImageFormat.");
+ isSupportedImageFormat = false;
+ }
+ fileStream.Close();
+ if (isSupportedImageFormat)
+ {
+ File.Move(localCacheFilePath2, localCacheFilePath);
+ return localCacheFilePath;
+ }
+ else
+ {
+ IOPath.FileTryDelete(localCacheFilePath2);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Log.Error(TAG, e, "GetImageAsync Fail.");
+ }
+
+ return default;
+ }
+
+ readonly ConcurrentDictionary>> get_image_pipeline = new();
+
+ public async Task GetImageAsync(
+ string requestUri,
+ string channelType,
+ CancellationToken cancellationToken)
+ {
+ if (get_image_pipeline.ContainsKey(channelType))
+ {
+ var pairs = get_image_pipeline[channelType];
+ if (pairs.ContainsKey(requestUri))
+ {
+ var findResult = await pairs[requestUri];
+ return FileOpenRead(findResult);
+ }
+ }
+ else
+ {
+ get_image_pipeline.TryAdd(channelType, new ConcurrentDictionary>());
+ }
+
+ var dirPath = Path.Combine(IOPath.CacheDirectory, "Images", channelType);
+ IOPath.DirCreateByNotExists(dirPath);
+ var fileName = Hashs.String.SHA256(requestUri) + FileEx.ImageSource;
+ var localCacheFilePath = Path.Combine(dirPath, fileName);
+
+ var pairs2 = get_image_pipeline[channelType];
+ var value = GetImageAsync_(requestUri, localCacheFilePath, cancellationToken);
+ pairs2.TryAdd(requestUri, value);
+ var result = await value;
+ pairs2.TryRemove(requestUri, out var _);
+ return FileOpenRead(result);
+
+ static Stream? FileOpenRead(string? p) => p == null ? null : File.OpenRead(p);
+ }
+ }
+}
\ No newline at end of file
diff --git a/System.Application.SteamTools.Services.CloudService/Services/CloudService/CloudServiceClientBase.cs b/System.Application.SteamTools.Services.CloudService/Services/CloudService/CloudServiceClientBase.cs
index 6bd43a98a..45692c9a6 100644
--- a/System.Application.SteamTools.Services.CloudService/Services/CloudService/CloudServiceClientBase.cs
+++ b/System.Application.SteamTools.Services.CloudService/Services/CloudService/CloudServiceClientBase.cs
@@ -10,7 +10,7 @@ using System.Threading.Tasks;
namespace System.Application.Services.CloudService
{
- public abstract class CloudServiceClientBase : HttpService, ICloudServiceClient, IApiConnectionPlatformHelper
+ public abstract class CloudServiceClientBase : GeneralHttpClientFactory, ICloudServiceClient, IApiConnectionPlatformHelper
{
public const string ClientName_ = "CloudServiceClient";
internal const string DefaultApiBaseUrl = Constants.Prefix_HTTPS + "api.steamtool.net";
diff --git a/System.Common.CoreLib/Net/Http/HttpService.cs b/System.Common.CoreLib/Net/Http/GeneralHttpClientFactory.cs
similarity index 87%
rename from System.Common.CoreLib/Net/Http/HttpService.cs
rename to System.Common.CoreLib/Net/Http/GeneralHttpClientFactory.cs
index 40bfd6a73..26f0092f3 100644
--- a/System.Common.CoreLib/Net/Http/HttpService.cs
+++ b/System.Common.CoreLib/Net/Http/GeneralHttpClientFactory.cs
@@ -2,13 +2,16 @@
namespace System.Net.Http
{
- public abstract class HttpService
+ ///
+ /// 通用 工厂
+ ///
+ public abstract class GeneralHttpClientFactory
{
protected readonly ILogger logger;
protected readonly IHttpPlatformHelper http_helper;
readonly IHttpClientFactory _clientFactory;
- public HttpService(
+ public GeneralHttpClientFactory(
ILogger logger,
IHttpPlatformHelper http_helper,
IHttpClientFactory clientFactory)
diff --git a/System.Common.CoreLib/Net/Http/HttpPlatformHelper.cs b/System.Common.CoreLib/Net/Http/HttpPlatformHelper.cs
index 04b7614be..24d3e8b24 100644
--- a/System.Common.CoreLib/Net/Http/HttpPlatformHelper.cs
+++ b/System.Common.CoreLib/Net/Http/HttpPlatformHelper.cs
@@ -1,4 +1,5 @@
using System.IO;
+using System.IO.FileFormats;
namespace System.Net.Http
{
@@ -9,6 +10,17 @@ namespace System.Net.Http
public virtual string UserAgent => DefaultUserAgent;
+ public virtual string AcceptImages => "image/png, image/*; q=0.8";
+
+ static readonly Lazy mSupportedImageFormats = new(() => new ImageFormat[]
+ {
+ ImageFormat.JPEG,
+ ImageFormat.PNG,
+ ImageFormat.GIF,
+ });
+
+ public virtual ImageFormat[] SupportedImageFormats => mSupportedImageFormats.Value;
+
public (string filePath, string mime)? TryHandleUploadFile(Stream fileStream) => null;
}
}
\ No newline at end of file
diff --git a/System.Common.CoreLib/Net/Http/IHttpPlatformHelper.cs b/System.Common.CoreLib/Net/Http/IHttpPlatformHelper.cs
index c57aebe6a..156bab0fd 100644
--- a/System.Common.CoreLib/Net/Http/IHttpPlatformHelper.cs
+++ b/System.Common.CoreLib/Net/Http/IHttpPlatformHelper.cs
@@ -1,4 +1,5 @@
using System.IO;
+using System.IO.FileFormats;
namespace System.Net.Http
{
@@ -14,6 +15,9 @@ namespace System.Net.Http
///
string UserAgent { get; }
+ string AcceptImages { get; }
+
+ ImageFormat[] SupportedImageFormats { get; }
///
/// 尝试将需要上传的文件流[处理]后保存到临时路径中并返回信息
/// [处理]由客户端平台原生实现
diff --git a/System.Common.UnitTest/SetupFixture.cs b/System.Common.UnitTest/SetupFixture.cs
index 8ffc17741..b544cb067 100644
--- a/System.Common.UnitTest/SetupFixture.cs
+++ b/System.Common.UnitTest/SetupFixture.cs
@@ -29,6 +29,10 @@ namespace System
static void ConfigureServices(IServiceCollection services)
{
services.AddLogging(l => l.AddProvider(NUnitLoggerProvider.Instance));
+ services.Configure(o =>
+ {
+ o.MinLevel = LogLevel.Trace;
+ });
//services.AddPinyin();
}
}