186 lines
5.9 KiB
C#
186 lines
5.9 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Diagnostics;
|
|
|
|
namespace CORP_MAUI.Services;
|
|
|
|
/// <summary>
|
|
/// Сервис для кэширования изображений на диске (файловый кэш)
|
|
/// </summary>
|
|
public class ImageCacheService
|
|
{
|
|
private static ImageCacheService? _instance;
|
|
public static ImageCacheService Instance => _instance ??= new ImageCacheService();
|
|
|
|
// Кэш путей к файлам (URL -> FilePath)
|
|
private readonly ConcurrentDictionary<string, string> _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}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Получить путь к файлу из кэша или загрузить изображение
|
|
/// </summary>
|
|
public async Task<string?> GetOrLoadImageFileAsync(string? url, Func<string, Task<byte[]?>>? loader = null)
|
|
{
|
|
if (string.IsNullOrEmpty(url)) return null;
|
|
|
|
// 1. Проверяем кэш путей
|
|
if (_pathCache.TryGetValue(url, out string? cachedPath) && File.Exists(cachedPath))
|
|
{
|
|
return cachedPath;
|
|
}
|
|
|
|
// 2. Проверяем файл на диске
|
|
string filePath = GetCacheFilePath(url);
|
|
if (File.Exists(filePath))
|
|
{
|
|
var fileInfo = new FileInfo(filePath);
|
|
if (fileInfo.Length > 0 && fileInfo.LastWriteTime > DateTime.Now - _cacheExpiry)
|
|
{
|
|
_pathCache[url] = filePath;
|
|
return filePath;
|
|
}
|
|
try { File.Delete(filePath); } catch { }
|
|
}
|
|
|
|
// 3. Загружаем изображение
|
|
if (loader != null)
|
|
{
|
|
try
|
|
{
|
|
byte[]? imageBytes = await loader(url);
|
|
if (imageBytes != null && imageBytes.Length > 0)
|
|
{
|
|
await File.WriteAllBytesAsync(filePath, imageBytes);
|
|
_pathCache[url] = filePath;
|
|
return filePath;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Trace.WriteLine($"[ImageCacheService] Ошибка загрузки {url}: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Получить путь к кэшированному файлу (если существует)
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Проверить, есть ли изображение в кэше
|
|
/// </summary>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Получить путь к файлу кэша для URL
|
|
/// </summary>
|
|
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);
|
|
|
|
/// <summary>
|
|
/// Очистить устаревший кэш
|
|
/// </summary>
|
|
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}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Очистить весь кэш
|
|
/// </summary>
|
|
public void ClearCache()
|
|
{
|
|
_pathCache.Clear();
|
|
try
|
|
{
|
|
if (Directory.Exists(_diskCachePath))
|
|
{
|
|
foreach (var file in Directory.GetFiles(_diskCachePath))
|
|
{
|
|
try { File.Delete(file); } catch { }
|
|
}
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
}
|