Enhance image caching reliability and URL handling
This commit is contained in:
parent
272ecdac64
commit
4eecf3792d
@ -37,11 +37,16 @@ public class Post
|
||||
string? url = !string.IsNullOrEmpty(ImageUrl) ? ImageUrl : Image;
|
||||
if (string.IsNullOrEmpty(url)) return null;
|
||||
|
||||
// Handle protocol-relative URLs (e.g. //cdn.com/img.jpg)
|
||||
if (url.StartsWith("//"))
|
||||
{
|
||||
return $"https:{url}";
|
||||
}
|
||||
|
||||
// Если ссылка уже абсолютная - возвращаем как есть
|
||||
if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase)) return url;
|
||||
|
||||
// Если ссылка относительная - добавляем домен
|
||||
// Обрабатываем случай, когда url начинается со / или без
|
||||
string cleanUrl = url.TrimStart('/');
|
||||
return $"https://corp.robotail.pro/{cleanUrl}";
|
||||
}
|
||||
|
||||
@ -62,11 +62,14 @@ public partial class PostsPage
|
||||
try
|
||||
{
|
||||
Uri uri = new Uri(post.DisplayImageUrl);
|
||||
// Sanitize filename
|
||||
string fileName = Path.GetFileName(uri.LocalPath);
|
||||
// Sanitize filename & use extension from URL if available, else .jpg
|
||||
string extension = Path.GetExtension(uri.LocalPath);
|
||||
if (string.IsNullOrEmpty(extension)) extension = ".jpg";
|
||||
|
||||
string fileName = Path.GetFileNameWithoutExtension(uri.LocalPath);
|
||||
fileName = Path.GetInvalidFileNameChars().Aggregate(fileName, (current, invalidChar) => current.Replace(invalidChar, '_'));
|
||||
|
||||
string cacheFileName = $"post_img_{post.Id}_{fileName}";
|
||||
string cacheFileName = $"post_img_{post.Id}_{fileName}{extension}";
|
||||
string cacheFilePath = Path.Combine(FileSystem.CacheDirectory, cacheFileName);
|
||||
|
||||
bool isCached = false;
|
||||
@ -90,9 +93,28 @@ public partial class PostsPage
|
||||
byte[]? bytes = await ServiceProvider.ApiService.GetImageBytesAsync(post.DisplayImageUrl);
|
||||
if (bytes is { Length: > 0 })
|
||||
{
|
||||
await File.WriteAllBytesAsync(cacheFilePath, bytes);
|
||||
Trace.WriteLine($"[PostsPage] Сохранено в кэш: {cacheFilePath}");
|
||||
vm.LocalImagePath = cacheFilePath;
|
||||
// Basic signature check
|
||||
bool isLikelyImage = false;
|
||||
if (bytes.Length > 2)
|
||||
{
|
||||
if (bytes[0] == 0xFF && bytes[1] == 0xD8) isLikelyImage = true; // JPEG
|
||||
else if (bytes[0] == 0x89 && bytes[1] == 0x50) isLikelyImage = true; // PNG
|
||||
else isLikelyImage = true; // Others
|
||||
|
||||
// Check for HTML
|
||||
if (bytes.Length > 5 && bytes[0] == '<') isLikelyImage = false;
|
||||
}
|
||||
|
||||
if (isLikelyImage)
|
||||
{
|
||||
await File.WriteAllBytesAsync(cacheFilePath, bytes);
|
||||
Trace.WriteLine($"[PostsPage] Сохранено в кэш: {cacheFilePath}");
|
||||
vm.LocalImagePath = cacheFilePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
Trace.WriteLine($"[PostsPage] ⚠️ Received bytes are likely NOT an image (starts with {bytes[0]:X}), skipping cache.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,6 +64,13 @@ public class PostViewModel : INotifyPropertyChanged
|
||||
|
||||
if (!string.IsNullOrEmpty(_localImagePath) && File.Exists(_localImagePath))
|
||||
{
|
||||
// Basic check for empty files
|
||||
try
|
||||
{
|
||||
if (new FileInfo(_localImagePath).Length == 0) return null;
|
||||
}
|
||||
catch { return null; }
|
||||
|
||||
// Используем FromStream для надежности, чтобы не блокировать файл
|
||||
// и гарантировать чтение даже из системных папок
|
||||
return ImageSource.FromStream(() =>
|
||||
@ -72,9 +79,10 @@ public class PostViewModel : INotifyPropertyChanged
|
||||
{
|
||||
return new FileStream(_localImagePath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Stream.Null;
|
||||
Trace.WriteLine($"[PostViewModel] Error reading image {_localImagePath}: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user