XAML-Map-Control/MapControl/Shared/ImageLoader.cs

192 lines
6.5 KiB
C#
Raw Normal View History

2025-03-31 21:33:52 +02:00
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
2025-08-19 23:20:11 +02:00
using System.Threading;
2024-05-22 11:25:32 +02:00
#if WPF
using System.Windows.Media;
2021-11-17 23:17:11 +01:00
#elif UWP
using Windows.UI.Xaml.Media;
2024-05-22 11:25:32 +02:00
#elif WINUI
using Microsoft.UI.Xaml.Media;
#endif
namespace MapControl
{
public static partial class ImageLoader
{
2025-03-31 21:33:52 +02:00
private static ILogger logger;
private static ILogger Logger => logger ?? (logger = LoggerFactory?.CreateLogger(typeof(ImageLoader)));
public static ILoggerFactory LoggerFactory { get; set; }
/// <summary>
/// The System.Net.Http.HttpClient instance used to download images via a http or https Uri.
/// </summary>
2025-03-31 21:33:52 +02:00
public static HttpClient HttpClient { get; set; }
2023-08-25 18:43:30 +02:00
static ImageLoader()
{
HttpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };
2023-08-25 18:43:30 +02:00
HttpClient.DefaultRequestHeaders.Add("User-Agent", $"XAML-Map-Control/{typeof(ImageLoader).Assembly.GetName().Version}");
}
public static Task<ImageSource> LoadImageAsync(Uri uri, IProgress<double> progress = null)
{
return LoadImageAsync(uri, progress, CancellationToken.None);
}
2025-08-19 23:20:11 +02:00
public static async Task<ImageSource> LoadImageAsync(Uri uri, IProgress<double> progress, CancellationToken cancellationToken)
{
ImageSource image = null;
2022-11-30 17:59:38 +01:00
progress?.Report(0d);
try
{
if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
{
2025-08-19 23:20:11 +02:00
var response = await GetHttpResponseAsync(uri, progress, cancellationToken);
2025-01-02 13:44:46 +01:00
if (response?.Buffer != null)
{
image = await LoadImageAsync(response.Buffer);
}
}
else if (uri.IsFile || !uri.IsAbsoluteUri)
{
image = await LoadImageAsync(uri.IsAbsoluteUri ? uri.LocalPath : uri.OriginalString);
}
else
{
image = LoadImage(uri);
}
}
catch (Exception ex)
{
2025-04-01 16:11:53 +02:00
Logger?.LogError(ex, "Failed loading image from {uri}", uri);
}
2022-11-30 17:59:38 +01:00
progress?.Report(1d);
return image;
}
public static async Task<ImageSource> LoadImageAsync(byte[] buffer)
{
using (var stream = new MemoryStream(buffer))
{
return await LoadImageAsync(stream);
}
}
internal class HttpResponse
{
public byte[] Buffer { get; }
public TimeSpan? MaxAge { get; }
public HttpResponse(byte[] buffer, TimeSpan? maxAge)
{
Buffer = buffer;
MaxAge = maxAge;
}
}
2025-08-19 23:20:11 +02:00
internal static async Task<HttpResponse> GetHttpResponseAsync(Uri uri, IProgress<double> progress, CancellationToken cancellationToken)
{
HttpResponse response = null;
try
{
var completionOptions = progress != null ? HttpCompletionOption.ResponseHeadersRead : HttpCompletionOption.ResponseContentRead;
using (var responseMessage = await HttpClient.GetAsync(uri, completionOptions, cancellationToken).ConfigureAwait(false))
{
if (responseMessage.IsSuccessStatusCode)
{
2025-08-12 08:49:17 +02:00
byte[] buffer;
2019-07-22 01:06:59 +02:00
2025-08-12 08:49:17 +02:00
if (progress != null && responseMessage.Content.Headers.ContentLength.HasValue)
2019-07-22 01:06:59 +02:00
{
buffer = await responseMessage.Content.ReadAsByteArrayAsync(progress, cancellationToken).ConfigureAwait(false);
2025-08-12 08:49:17 +02:00
}
else
{
buffer = await responseMessage.Content.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(false);
2019-07-22 01:06:59 +02:00
}
response = new HttpResponse(buffer, responseMessage.Headers.CacheControl?.MaxAge);
}
else
{
2025-03-31 21:33:52 +02:00
Logger?.LogWarning("{uri}: {status} {reason}", uri, (int)responseMessage.StatusCode, responseMessage.ReasonPhrase);
}
}
}
2025-08-21 12:14:31 +02:00
catch (OperationCanceledException ex)
2025-08-19 23:20:11 +02:00
{
if (ex.CancellationToken.IsCancellationRequested)
2025-08-21 12:14:31 +02:00
{
Logger?.LogTrace("Cancelled loading image from {uri}", uri);
2025-08-21 12:14:31 +02:00
}
else
{
Logger?.LogError(ex, "Failed loading image from {uri}", uri);
2025-08-21 12:14:31 +02:00
}
2025-08-19 23:20:11 +02:00
}
catch (Exception ex)
{
2025-04-01 16:11:53 +02:00
Logger?.LogError(ex, "Failed loading image from {uri}", uri);
}
return response;
}
}
internal static class HttpContentExtensions
{
public static async Task<byte[]> ReadAsByteArrayAsync(this HttpContent content, IProgress<double> progress, CancellationToken cancellationToken)
{
var length = (int)content.Headers.ContentLength.Value;
var buffer = new byte[length];
using (var stream = await content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false))
{
int offset = 0;
int read;
while (offset < length &&
(read = await stream.ReadAsync(buffer, offset, length - offset, cancellationToken).ConfigureAwait(false)) > 0)
{
cancellationToken.ThrowIfCancellationRequested();
offset += read;
if (offset < length) // 1.0 reported by caller
{
progress.Report((double)offset / length);
}
}
}
return buffer;
}
#if !NET
public static Task<byte[]> ReadAsByteArrayAsync(this HttpContent content, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
return content.ReadAsByteArrayAsync();
}
public static Task<Stream> ReadAsStreamAsync(this HttpContent content, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
return content.ReadAsStreamAsync();
}
#endif
}
}