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();
}
}