XAML-Map-Control/MapControl/Shared/ImageLoader.cs
2025-12-25 20:01:08 +01:00

151 lines
4.7 KiB
C#

using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
#if WPF
using System.Windows.Media;
#elif UWP
using Windows.UI.Xaml.Media;
#elif WINUI
using Microsoft.UI.Xaml.Media;
#elif AVALONIA
using ImageSource = Avalonia.Media.IImage;
#endif
namespace MapControl
{
public static partial class ImageLoader
{
private static ILogger logger;
private static ILogger Logger => logger ??= LoggerFactory?.CreateLogger(typeof(ImageLoader));
public static ILoggerFactory LoggerFactory { get; set; }
/// <summary>
/// The System.Net.Http.HttpClient instance used to download images.
/// </summary>
public static HttpClient HttpClient { get; set; }
static ImageLoader()
{
HttpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };
HttpClient.DefaultRequestHeaders.Add("User-Agent", $"XAML-Map-Control/{typeof(ImageLoader).Assembly.GetName().Version}");
}
public static async Task<ImageSource> LoadImageAsync(byte[] buffer)
{
using var stream = new MemoryStream(buffer);
return await LoadImageAsync(stream);
}
public static async Task<ImageSource> LoadImageAsync(Uri uri, IProgress<double> progress = null)
{
ImageSource image = null;
progress?.Report(0d);
try
{
if (!uri.IsAbsoluteUri)
{
image = await LoadImageAsync(uri.OriginalString);
}
else if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
{
var buffer = await GetHttpContent(uri, progress);
if (buffer != null)
{
image = await LoadImageAsync(buffer);
}
}
else if (uri.IsFile)
{
image = await LoadImageAsync(uri.LocalPath);
}
else
{
image = LoadImage(uri);
}
}
catch (Exception ex)
{
Logger?.LogError(ex, "Failed loading {uri}", uri);
}
progress?.Report(1d);
return image;
}
public static async Task<HttpResponseMessage> GetHttpResponseAsync(Uri uri, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead)
{
try
{
var response = await HttpClient.GetAsync(uri, completionOption).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
return response;
}
Logger?.LogWarning("{status} ({reason}) from {uri}", (int)response.StatusCode, response.ReasonPhrase, uri);
response.Dispose();
}
catch (TaskCanceledException)
{
Logger?.LogWarning("Timeout while loading {uri}", uri);
}
catch (Exception ex)
{
Logger?.LogError(ex, "Failed loading {uri}", uri);
}
return null;
}
private static async Task<byte[]> GetHttpContent(Uri uri, IProgress<double> progress)
{
var completionOption = progress != null ? HttpCompletionOption.ResponseHeadersRead : HttpCompletionOption.ResponseContentRead;
using var response = await GetHttpResponseAsync(uri, completionOption).ConfigureAwait(false);
if (response == null)
{
return null;
}
var content = response.Content;
var contentLength = content.Headers.ContentLength;
if (progress == null || !contentLength.HasValue)
{
return await content.ReadAsByteArrayAsync().ConfigureAwait(false);
}
var length = (int)contentLength.Value;
var buffer = new byte[length];
using (var stream = await content.ReadAsStreamAsync().ConfigureAwait(false))
{
int offset = 0;
int read;
while (offset < length &&
(read = await stream.ReadAsync(buffer, offset, length - offset).ConfigureAwait(false)) > 0)
{
offset += read;
if (offset < length) // 1.0 reported by caller
{
progress.Report((double)offset / length);
}
}
}
return buffer;
}
}
}