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; } /// /// The System.Net.Http.HttpClient instance used to download images. /// 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 LoadImageAsync(byte[] buffer) { using var stream = new MemoryStream(buffer); return await LoadImageAsync(stream); } public static async Task LoadImageAsync(Uri uri, IProgress 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 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 GetHttpContent(Uri uri, IProgress 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; } } }