Enhance ImageDownloader to improve error handling and logging
ci / build-backend (push) Successful in 1m33s
ci / build-frontend (push) Successful in 57s
ci / tests (push) Failing after 15s
ci / sonar (push) Skipped

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.
This commit is contained in:
Leonid Pershin
2026-07-30 01:48:07 +03:00
parent bd89abf24b
commit 10e05b8689
@@ -1,9 +1,18 @@
using Microsoft.Extensions.Logging;
using TeleWave.Application.Common.Interfaces; using TeleWave.Application.Common.Interfaces;
namespace TeleWave.Infrastructure.Media; namespace TeleWave.Infrastructure.Media;
/// <summary>Скачивает изображение по URL через HTTP-клиент «metadata» и определяет расширение.</summary> /// <summary>
public sealed class ImageDownloader(IHttpClientFactory httpFactory) : IImageDownloader /// Скачивает изображение по URL через HTTP-клиент «metadata» и определяет расширение.
///
/// Любая неудача — это null, а не исключение: постер не тот повод, чтобы ронять применение
/// метаданных. Но молчать о ней нельзя — снаружи «постера нет» неотличимо от «постера не было
/// в источнике», а причина обычно сетевая: у TMDb картинки лежат на отдельном хосте
/// (<c>image.tmdb.org</c>), и он бывает недоступен там, где сам API работает.
/// </summary>
public sealed class ImageDownloader(IHttpClientFactory httpFactory, ILogger<ImageDownloader> logger)
: IImageDownloader
{ {
public async Task<DownloadedImage?> DownloadAsync( public async Task<DownloadedImage?> DownloadAsync(
string url, string url,
@@ -17,22 +26,40 @@ public sealed class ImageDownloader(IHttpClientFactory httpFactory) : IImageDown
!Uri.TryCreate(url, UriKind.Absolute, out var uri) !Uri.TryCreate(url, UriKind.Absolute, out var uri)
|| (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps) || (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps)
) )
{
logger.LogWarning("Картинка не скачана: недопустимый адрес {Url}", url);
return null; return null;
}
try try
{ {
var client = httpFactory.CreateClient("metadata"); var client = httpFactory.CreateClient("metadata");
using var response = await client.GetAsync(uri, cancellationToken); using var response = await client.GetAsync(uri, cancellationToken);
if (!response.IsSuccessStatusCode) if (!response.IsSuccessStatusCode)
{
logger.LogWarning(
"Картинка не скачана: {Url} ответил {Status}",
url,
(int)response.StatusCode
);
return null; return null;
}
var ext = ExtensionFor(url, response.Content.Headers.ContentType?.MediaType); var ext = ExtensionFor(url, response.Content.Headers.ContentType?.MediaType);
var bytes = await response.Content.ReadAsByteArrayAsync(cancellationToken); 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) catch (Exception ex)
when (ex is HttpRequestException or TaskCanceledException or IOException) when (ex is HttpRequestException or TaskCanceledException or IOException)
{ {
// Самая частая причина — недоступный хост картинок: он отдельный от хоста API.
logger.LogWarning(ex, "Картинка не скачана: {Url} недоступен", url);
return null; return null;
} }
} }