using System.Collections.Concurrent;
using System.Diagnostics;
namespace CORP_MAUI.Services;
///
/// Сервис для кэширования изображений на диске (файловый кэш)
///
public class ImageCacheService
{
private static ImageCacheService? _instance;
public static ImageCacheService Instance => _instance ??= new ImageCacheService();
// Кэш путей к файлам (URL -> FilePath)
private readonly ConcurrentDictionary _pathCache = new();
// Путь к папке кэша на диске
private readonly string _diskCachePath;
// Максимальное время жизни кэша
private readonly TimeSpan _cacheExpiry = TimeSpan.FromDays(7);
private ImageCacheService()
{
_diskCachePath = Path.Combine(FileSystem.CacheDirectory, "avatars");
// Создаём папку кэша, если не существует
if (!Directory.Exists(_diskCachePath))
{
Directory.CreateDirectory(_diskCachePath);
}
Trace.WriteLine($"[ImageCacheService] Initialized. Cache path: {_diskCachePath}");
}
///
/// Получить путь к файлу из кэша или загрузить изображение
///
public async Task GetOrLoadImageFileAsync(string? url, Func>? loader = null)
{
if (string.IsNullOrEmpty(url))
{
Trace.WriteLine($"[ImageCacheService] URL пустой, пропускаем");
return null;
}
Trace.WriteLine($"[ImageCacheService] >>> GetOrLoadImageFileAsync: {url}");
// 1. Проверяем кэш путей
if (_pathCache.TryGetValue(url, out string? cachedPath) && File.Exists(cachedPath))
{
Trace.WriteLine($"[ImageCacheService] ✅ Найден в кэше путей: {cachedPath}");
return cachedPath;
}
// 2. Проверяем файл на диске
string filePath = GetCacheFilePath(url);
Trace.WriteLine($"[ImageCacheService] Ожидаемый путь: {filePath}");
if (File.Exists(filePath))
{
var fileInfo = new FileInfo(filePath);
Trace.WriteLine($"[ImageCacheService] Файл существует, размер: {fileInfo.Length}, время: {fileInfo.LastWriteTime}");
if (fileInfo.Length > 0 && fileInfo.LastWriteTime > DateTime.Now - _cacheExpiry)
{
_pathCache[url] = filePath;
Trace.WriteLine($"[ImageCacheService] ✅ Загружен с диска: {filePath}");
return filePath;
}
try { File.Delete(filePath); } catch { }
}
// 3. Загружаем изображение
if (loader != null)
{
try
{
Trace.WriteLine($"[ImageCacheService] >>> Загрузка изображения через loader...");
byte[]? imageBytes = await loader(url);
if (imageBytes != null && imageBytes.Length > 0)
{
Trace.WriteLine($"[ImageCacheService] Получено {imageBytes.Length} байт, сохраняем в {filePath}");
await File.WriteAllBytesAsync(filePath, imageBytes);
_pathCache[url] = filePath;
Trace.WriteLine($"[ImageCacheService] ✅ Сохранено и добавлено в кэш");
return filePath;
}
else
{
Trace.WriteLine($"[ImageCacheService] ⚠️ Loader вернул пустые данные");
}
}
catch (Exception ex)
{
Trace.WriteLine($"[ImageCacheService] ❌ Ошибка загрузки {url}: {ex.Message}");
}
}
else
{
Trace.WriteLine($"[ImageCacheService] ⚠️ Loader не задан");
}
return null;
}
///
/// Получить путь к кэшированному файлу (если существует)
///
public string? GetCachedFilePath(string? url)
{
if (string.IsNullOrEmpty(url)) return null;
if (_pathCache.TryGetValue(url, out string? cachedPath) && File.Exists(cachedPath))
{
return cachedPath;
}
string filePath = GetCacheFilePath(url);
if (File.Exists(filePath))
{
var fileInfo = new FileInfo(filePath);
if (fileInfo.Length > 0)
{
_pathCache[url] = filePath;
return filePath;
}
}
return null;
}
///
/// Проверить, есть ли изображение в кэше
///
public bool IsCached(string? url)
{
if (string.IsNullOrEmpty(url)) return false;
if (_pathCache.TryGetValue(url, out string? path) && File.Exists(path))
return true;
string filePath = GetCacheFilePath(url);
return File.Exists(filePath);
}
///
/// Получить путь к файлу кэша для URL
///
private string GetCacheFilePath(string url)
{
using var md5 = System.Security.Cryptography.MD5.Create();
byte[] hashBytes = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(url));
string hash = BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
return Path.Combine(_diskCachePath, $"{hash}.jpg");
}
// Для обратной совместимости
public string? GetFromCache(string? url) => GetCachedFilePath(url);
///
/// Очистить устаревший кэш
///
public void CleanExpiredCache()
{
try
{
if (!Directory.Exists(_diskCachePath)) return;
var expiredFiles = Directory.GetFiles(_diskCachePath)
.Where(f => File.GetLastWriteTime(f) < DateTime.Now - _cacheExpiry)
.ToList();
foreach (var file in expiredFiles)
{
try { File.Delete(file); } catch { }
}
if (expiredFiles.Count > 0)
{
Trace.WriteLine($"[ImageCacheService] Удалено {expiredFiles.Count} устаревших файлов");
}
}
catch (Exception ex)
{
Trace.WriteLine($"[ImageCacheService] Ошибка очистки: {ex.Message}");
}
}
///
/// Очистить весь кэш
///
public void ClearCache()
{
_pathCache.Clear();
try
{
if (Directory.Exists(_diskCachePath))
{
foreach (var file in Directory.GetFiles(_diskCachePath))
{
try { File.Delete(file); } catch { }
}
}
}
catch { }
}
}