From 10e05b86890004a94e19b7d15f90cc70823e3ca2 Mon Sep 17 00:00:00 2001 From: Leonid Pershin Date: Thu, 30 Jul 2026 01:48:07 +0300 Subject: [PATCH] Enhance ImageDownloader to improve error handling and logging Updated the ImageDownloader class to include detailed logging for various failure scenarios during image download attempts. Added checks for invalid URLs, unsuccessful HTTP responses, and empty content responses, ensuring that all failures are logged with appropriate warnings. This enhancement improves the robustness of the image downloading process and provides better insights into potential issues. --- .../Media/ImageDownloader.cs | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/backend/src/TeleWave.Infrastructure/Media/ImageDownloader.cs b/backend/src/TeleWave.Infrastructure/Media/ImageDownloader.cs index 1c2b90c..aa41fa5 100644 --- a/backend/src/TeleWave.Infrastructure/Media/ImageDownloader.cs +++ b/backend/src/TeleWave.Infrastructure/Media/ImageDownloader.cs @@ -1,9 +1,18 @@ +using Microsoft.Extensions.Logging; using TeleWave.Application.Common.Interfaces; namespace TeleWave.Infrastructure.Media; -/// Скачивает изображение по URL через HTTP-клиент «metadata» и определяет расширение. -public sealed class ImageDownloader(IHttpClientFactory httpFactory) : IImageDownloader +/// +/// Скачивает изображение по URL через HTTP-клиент «metadata» и определяет расширение. +/// +/// Любая неудача — это null, а не исключение: постер не тот повод, чтобы ронять применение +/// метаданных. Но молчать о ней нельзя — снаружи «постера нет» неотличимо от «постера не было +/// в источнике», а причина обычно сетевая: у TMDb картинки лежат на отдельном хосте +/// (image.tmdb.org), и он бывает недоступен там, где сам API работает. +/// +public sealed class ImageDownloader(IHttpClientFactory httpFactory, ILogger logger) + : IImageDownloader { public async Task DownloadAsync( string url, @@ -17,22 +26,40 @@ public sealed class ImageDownloader(IHttpClientFactory httpFactory) : IImageDown !Uri.TryCreate(url, UriKind.Absolute, out var uri) || (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps) ) + { + logger.LogWarning("Картинка не скачана: недопустимый адрес {Url}", url); return null; + } try { var client = httpFactory.CreateClient("metadata"); using var response = await client.GetAsync(uri, cancellationToken); if (!response.IsSuccessStatusCode) + { + logger.LogWarning( + "Картинка не скачана: {Url} ответил {Status}", + url, + (int)response.StatusCode + ); return null; + } var ext = ExtensionFor(url, response.Content.Headers.ContentType?.MediaType); var bytes = await response.Content.ReadAsByteArrayAsync(cancellationToken); - return bytes.Length == 0 ? null : new DownloadedImage(bytes, ext); + if (bytes.Length == 0) + { + logger.LogWarning("Картинка не скачана: {Url} вернул пустой ответ", url); + return null; + } + + return new DownloadedImage(bytes, ext); } catch (Exception ex) when (ex is HttpRequestException or TaskCanceledException or IOException) { + // Самая частая причина — недоступный хост картинок: он отдельный от хоста API. + logger.LogWarning(ex, "Картинка не скачана: {Url} недоступен", url); return null; } }