public ImageLoaderTask(ImageLoader imageLoader, ILoadableImageSource imageSource, ImageDecoder?decoder) { _imageLoader = imageLoader; ImageSource = imageSource; _decoder = decoder; _memoryCache = _imageLoader.MemoryCache; CancellationTokenSource = new CancellationTokenSource(); string?sourceKey = imageSource.Source.Key; if (sourceKey != null) { KeyRaw = sourceKey; } else { // If there's no key then we can't use the memory cache _memoryCache = null; KeyRaw = string.Concat("Stream_", Guid.NewGuid().ToString("N")); } if (string.IsNullOrWhiteSpace(KeyRaw)) { throw new Exception("Key cannot be empty"); } if (imageSource is IBitmapImageSource bitmapImageSource && (bitmapImageSource.DecodePixelWidth > 0 || bitmapImageSource.DecodePixelHeight > 0)) { KeyDownsamplingOnly = $";{bitmapImageSource.DecodePixelWidth}x{bitmapImageSource.DecodePixelHeight}"; }
public override async Task <Stream> ResolveAsync(ILoadableImageSource imageSource, IImageLoader imageLoader, CancellationToken token) { string identifier = Uri.ToString(); Stream stream = await imageLoader.DownloadCache.DownloadAndCacheIfNeededAsync(identifier, imageSource, token).ConfigureAwait(false); if (token.IsCancellationRequested) { stream.TryDispose(); token.ThrowIfCancellationRequested(); } return(stream); }
protected virtual int GetDefaultPriority(ILoadableImageSource source) { if (source == ImageSource.ApplicationBundle || source == ImageSource.CompiledResource) { return((int)LoadingPriority.Normal + 2); if (source == ImageSource.Filepath) { return((int)LoadingPriority.Normal + 1); } return((int)LoadingPriority.Normal); } #endif }
public virtual async Task <Stream> DownloadAndCacheIfNeededAsync(string url, ILoadableImageSource imageSource, CancellationToken token) { string filename = MD5Helper.MD5(url); bool allowDiskCaching = AllowDiskCaching(_imageLoader.CacheType); IDiskCache?diskCache = _imageLoader.DiskCache; TimeSpan duration = _imageLoader.DiskCacheDuration; if (allowDiskCaching) { Stream?diskStream = await diskCache !.TryGetStreamAsync(filename).ConfigureAwait(false); token.ThrowIfCancellationRequested(); if (diskStream != null) { return(diskStream); } } var downloadInfo = new DownloadInformation(duration); #if LATER parameters.OnDownloadStarted?.Invoke(downloadInfo); #endif byte[] responseBytes = await Retry.DoAsync( async() => await DownloadAsync(url, token, _imageLoader.HttpClient, imageSource, downloadInfo).ConfigureAwait(false), TimeSpan.FromMilliseconds(_imageLoader.RetryDelayInMs), _imageLoader.RetryCount, () => _imageLoader.Logger?.Debug($"Retry download: {url}")).ConfigureAwait(false); if (responseBytes == null) { throw new HttpRequestException("No Content"); } if (allowDiskCaching) { await diskCache !.AddToSavingQueueIfNotExistsAsync(filename, responseBytes, downloadInfo.CacheValidity).ConfigureAwait(false); } token.ThrowIfCancellationRequested(); return(new MemoryStream(responseBytes, false)); }
public abstract Task <Stream> ResolveAsync(ILoadableImageSource imageSource, IImageLoader imageLoader, CancellationToken token);
protected virtual async Task <byte[]> DownloadAsync(string url, CancellationToken token, HttpClient client, ILoadableImageSource imageSource, DownloadInformation downloadInfo) { await Task.Delay(25, token).ConfigureAwait(false); token.ThrowIfCancellationRequested(); using (var httpHeadersTimeoutTokenSource = new CancellationTokenSource()) using (var headersTimeoutTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, httpHeadersTimeoutTokenSource.Token)) { httpHeadersTimeoutTokenSource.CancelAfter(TimeSpan.FromSeconds(_imageLoader.HttpHeadersTimeout)); try { CancellationToken headerTimeoutToken = headersTimeoutTokenSource.Token; using (var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, headerTimeoutToken).ConfigureAwait(false)) { headerTimeoutToken.ThrowIfCancellationRequested(); if (!response.IsSuccessStatusCode) { if (response.Content == null) { throw new DownloadHttpStatusCodeException(response.StatusCode); } using (response.Content) { string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); throw new DownloadHttpStatusCodeException(response.StatusCode, content); } } if (response.Content == null) { throw new DownloadException("No content"); } string?mediaType = response.Content.Headers?.ContentType?.MediaType; if (!string.IsNullOrWhiteSpace(mediaType) && !mediaType !.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) { if (InvalidContentTypes.Any(v => mediaType.StartsWith(v, StringComparison.OrdinalIgnoreCase))) { throw new DownloadException($"Invalid response content type ({mediaType})"); } } if (_imageLoader.TryToReadDiskCacheDurationFromHttpHeaders && response.Headers?.CacheControl?.MaxAge != null && response.Headers.CacheControl.MaxAge > TimeSpan.Zero) { downloadInfo.CacheValidity = response.Headers.CacheControl.MaxAge.Value; } using (var httpReadTimeoutTokenSource = new CancellationTokenSource()) using (var readTimeoutTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, httpReadTimeoutTokenSource.Token)) { CancellationToken readTimeoutToken = readTimeoutTokenSource.Token; CancellationToken httpReadTimeoutToken = httpReadTimeoutTokenSource.Token; int total = (int)(response.Content.Headers?.ContentLength ?? -1); httpReadTimeoutTokenSource.CancelAfter(_imageLoader.HttpClient.Timeout); readTimeoutToken.ThrowIfCancellationRequested(); try { using (var outputStream = new MemoryStream()) using (var sourceStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) { httpReadTimeoutToken.Register(() => sourceStream.TryDispose()); int totalRead = 0; byte[] buffer = new byte[_imageLoader.HttpReadBufferSize]; int read = 0; int currentProgress = 1; #if LATER imageSource.RaiseDownloadProgress(new DownloadProgressEventArgs(currentProgress)); #endif while ((read = await sourceStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false)) > 0) { readTimeoutToken.ThrowIfCancellationRequested(); outputStream.Write(buffer, 0, read); totalRead += read; if (total > 0) { currentProgress = totalRead * 100 / total; } else { currentProgress = ++currentProgress; } if (currentProgress < 1) { currentProgress = 1; } if (currentProgress > 99) { currentProgress = 99; } #if LATER imageSource.RaiseDownloadProgress(new DownloadProgressEventArgs(currentProgress)); #endif } if (outputStream.Length == 0) { throw new DownloadException("Zero length stream"); } if (outputStream.Length < 32) { throw new DownloadException("Invalid stream"); } currentProgress = 100; #if LATER imageSource.RaiseDownloadProgress(new DownloadProgressEventArgs(currentProgress)); #endif return(outputStream.ToArray()); } } catch (Exception ex) when(ex is OperationCanceledException || ex is ObjectDisposedException) { if (httpReadTimeoutTokenSource.IsCancellationRequested) { throw new DownloadReadTimeoutException(); } throw; } } } } catch (OperationCanceledException) { if (httpHeadersTimeoutTokenSource.IsCancellationRequested) { throw new DownloadHeadersTimeoutException(); } else { throw; } } } }
public override Task <Stream> ResolveAsync(ILoadableImageSource imageSource, IImageLoader imageLoader, CancellationToken token) => throw new System.NotImplementedException();