using System; using System.IO; using System.Net.Http; using System.Threading.Tasks; using Avalonia.Logging; using Avalonia.Media.Imaging; using Avalonia.Platform; namespace DownKyi.CustomControl.AsyncImageLoader.Loaders; public class BaseWebImageLoader : IAsyncImageLoader { private readonly ParametrizedLogger? _logger; private readonly bool _shouldDisposeHttpClient; /// /// Initializes a new instance with new instance /// public BaseWebImageLoader() : this(new HttpClient(), true) { } /// /// Initializes a new instance with the provided , and specifies whether that /// should be disposed when this instance is disposed. /// /// The HttpMessageHandler responsible for processing the HTTP response messages. /// /// true if the inner handler should be disposed of by Dispose; false if you intend to /// reuse the HttpClient. /// public BaseWebImageLoader(HttpClient httpClient, bool disposeHttpClient) { HttpClient = httpClient; _shouldDisposeHttpClient = disposeHttpClient; _logger = Logger.TryGet(LogEventLevel.Error, ImageLoader.AsyncImageLoaderLogArea); } protected HttpClient HttpClient { get; } /// public virtual async Task ProvideImageAsync(string url) { return await LoadAsync(url).ConfigureAwait(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Attempts to load bitmap /// /// Target url /// Bitmap protected virtual async Task LoadAsync(string url) { var internalOrCachedBitmap = await LoadFromLocalAsync(url).ConfigureAwait(false) ?? await LoadFromInternalAsync(url).ConfigureAwait(false) ?? await LoadFromGlobalCache(url).ConfigureAwait(false); if (internalOrCachedBitmap != null) return internalOrCachedBitmap; try { var externalBytes = await LoadDataFromExternalAsync(url).ConfigureAwait(false); if (externalBytes == null) return null; using var memoryStream = new MemoryStream(externalBytes); var bitmap = new Bitmap(memoryStream); await SaveToGlobalCache(url, externalBytes).ConfigureAwait(false); return bitmap; } catch (Exception e) { _logger?.Log(this, "Failed to resolve image: {RequestUri}\nException: {Exception}", url, e); return null; } } /// /// the url maybe is local file url,so if file exists ,we got a Bitmap /// /// /// private Task LoadFromLocalAsync(string url) { return Task.FromResult(File.Exists(url) ? new Bitmap(url) : null); } /// /// Receives image bytes from an internal source (for example, from the disk). /// This data will be NOT cached globally (because it is assumed that it is already in internal source us and does not /// require global caching) /// /// Target url /// Bitmap protected virtual Task LoadFromInternalAsync(string url) { try { var uri = url.StartsWith("/") ? new Uri(url, UriKind.Relative) : new Uri(url, UriKind.RelativeOrAbsolute); if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps) return Task.FromResult(null); if (uri is { IsAbsoluteUri: true, IsFile: true }) return Task.FromResult(new Bitmap(uri.LocalPath))!; return Task.FromResult(new Bitmap(AssetLoader.Open(uri)))!; } catch (Exception e) { _logger?.Log(this, "Failed to resolve image from request with uri: {RequestUri}\nException: {Exception}", url, e); return Task.FromResult(null); } } /// /// Receives image bytes from an external source (for example, from the Internet). /// This data will be cached globally (if required by the current implementation) /// /// Target url /// Image bytes protected virtual async Task LoadDataFromExternalAsync(string url) { try { return await HttpClient.GetByteArrayAsync(url).ConfigureAwait(false); } catch (Exception e) { _logger?.Log(this, "Failed to resolve image from request with uri: {RequestUri}\nException: {Exception}", url, e); return null; } } /// /// Attempts to load image from global cache (if it is stored before) /// /// Target url /// Bitmap protected virtual Task LoadFromGlobalCache(string url) { // Current implementation does not provide global caching return Task.FromResult(null); } /// /// Attempts to load image from global cache (if it is stored before) /// /// Target url /// Bytes to save /// Bitmap protected virtual Task SaveToGlobalCache(string url, byte[] imageBytes) { // Current implementation does not provide global caching return Task.CompletedTask; } ~BaseWebImageLoader() { Dispose(false); } protected virtual void Dispose(bool disposing) { if (disposing && _shouldDisposeHttpClient) HttpClient.Dispose(); } }