mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2026-02-07 16:24:19 +01:00
Unified ImageFileCache implementations
This commit is contained in:
parent
8cb20f0544
commit
f9347edf19
71
MapControl/Shared/ImageFileCache.cs
Normal file
71
MapControl/Shared/ImageFileCache.cs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
|
||||
// © 2021 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace MapControl.Caching
|
||||
{
|
||||
/// <summary>
|
||||
/// Image Cache implementation based on local image files.
|
||||
/// The only valid data type for cached values is MapControl.ImageCacheItem.
|
||||
/// </summary>
|
||||
public partial class ImageFileCache
|
||||
{
|
||||
private const string expiresTag = "EXPIRES:";
|
||||
|
||||
private readonly string rootDirectory;
|
||||
|
||||
public ImageFileCache(string directory)
|
||||
{
|
||||
if (string.IsNullOrEmpty(directory))
|
||||
{
|
||||
throw new ArgumentException("The directory argument must not be null or empty.", nameof(directory));
|
||||
}
|
||||
|
||||
rootDirectory = directory;
|
||||
Debug.WriteLine("Created ImageFileCache in " + rootDirectory);
|
||||
}
|
||||
|
||||
private string GetPath(string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Path.Combine(rootDirectory, Path.Combine(key.Split('/', ':', ';', ',')));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("ImageFileCache: Invalid key {0}/{1}: {2}", rootDirectory, key, ex.Message);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static DateTime ReadExpiration(ref byte[] buffer)
|
||||
{
|
||||
DateTime? expiration = ReadExpiration(buffer);
|
||||
|
||||
if (expiration.HasValue)
|
||||
{
|
||||
Array.Resize(ref buffer, buffer.Length - 16);
|
||||
return expiration.Value;
|
||||
}
|
||||
|
||||
return DateTime.Today;
|
||||
}
|
||||
|
||||
private static DateTime? ReadExpiration(byte[] buffer)
|
||||
{
|
||||
if (buffer.Length >= 16 &&
|
||||
Encoding.ASCII.GetString(buffer, buffer.Length - 16, 8) == expiresTag)
|
||||
{
|
||||
return new DateTime(BitConverter.ToInt64(buffer, buffer.Length - 8), DateTimeKind.Utc);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,23 +12,8 @@ using Windows.Storage.Streams;
|
|||
|
||||
namespace MapControl.Caching
|
||||
{
|
||||
public class ImageFileCache : IImageCache
|
||||
public partial class ImageFileCache : IImageCache
|
||||
{
|
||||
private const string expiresTag = "EXPIRES:";
|
||||
|
||||
private readonly string rootDirectory;
|
||||
|
||||
public ImageFileCache(string directory)
|
||||
{
|
||||
if (string.IsNullOrEmpty(directory))
|
||||
{
|
||||
throw new ArgumentException("The directory argument must not be null or empty.", nameof(directory));
|
||||
}
|
||||
|
||||
rootDirectory = directory;
|
||||
Debug.WriteLine("Created ImageFileCache in " + rootDirectory);
|
||||
}
|
||||
|
||||
public async Task<ImageCacheItem> GetAsync(string key)
|
||||
{
|
||||
ImageCacheItem imageCacheItem = null;
|
||||
|
|
@ -39,7 +24,7 @@ namespace MapControl.Caching
|
|||
if (path != null && File.Exists(path))
|
||||
{
|
||||
var buffer = await File.ReadAllBytesAsync(path);
|
||||
var expiration = GetExpiration(ref buffer);
|
||||
var expiration = ReadExpiration(ref buffer);
|
||||
|
||||
imageCacheItem = new ImageCacheItem
|
||||
{
|
||||
|
|
@ -83,32 +68,5 @@ namespace MapControl.Caching
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetPath(string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Path.Combine(rootDirectory, Path.Combine(key.Split('/', ':', ';', ',')));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("ImageFileCache: Invalid key {0}/{1}: {2}", rootDirectory, key, ex.Message);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static DateTime GetExpiration(ref byte[] buffer)
|
||||
{
|
||||
DateTime expiration = DateTime.Today;
|
||||
|
||||
if (buffer.Length > 16 && Encoding.ASCII.GetString(buffer, buffer.Length - 16, 8) == expiresTag)
|
||||
{
|
||||
expiration = new DateTime(BitConverter.ToInt64(buffer, buffer.Length - 8), DateTimeKind.Utc);
|
||||
Array.Resize(ref buffer, buffer.Length - 16);
|
||||
}
|
||||
|
||||
return expiration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,6 +77,9 @@
|
|||
<Compile Include="..\Shared\HyperlinkText.cs">
|
||||
<Link>HyperlinkText.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\ImageFileCache.cs">
|
||||
<Link>ImageFileCache.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\ImageLoader.cs">
|
||||
<Link>ImageLoader.cs</Link>
|
||||
</Compile>
|
||||
|
|
|
|||
|
|
@ -19,27 +19,13 @@ namespace MapControl.Caching
|
|||
/// ObjectCache implementation based on local image files.
|
||||
/// The only valid data type for cached values is MapControl.ImageCacheItem.
|
||||
/// </summary>
|
||||
public class ImageFileCache : ObjectCache
|
||||
public partial class ImageFileCache : ObjectCache
|
||||
{
|
||||
private const string expiresTag = "EXPIRES:";
|
||||
|
||||
private static readonly FileSystemAccessRule fullControlRule = new FileSystemAccessRule(
|
||||
new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null),
|
||||
FileSystemRights.FullControl, AccessControlType.Allow);
|
||||
|
||||
private readonly MemoryCache memoryCache = MemoryCache.Default;
|
||||
private readonly string rootDirectory;
|
||||
|
||||
public ImageFileCache(string directory)
|
||||
{
|
||||
if (string.IsNullOrEmpty(directory))
|
||||
{
|
||||
throw new ArgumentException("The directory argument must not be null or empty.", nameof(directory));
|
||||
}
|
||||
|
||||
rootDirectory = directory;
|
||||
Debug.WriteLine("Created ImageFileCache in " + rootDirectory);
|
||||
}
|
||||
|
||||
public Task Clean()
|
||||
{
|
||||
|
|
@ -115,7 +101,7 @@ namespace MapControl.Caching
|
|||
try
|
||||
{
|
||||
var buffer = File.ReadAllBytes(path);
|
||||
var expiration = GetExpiration(ref buffer);
|
||||
var expiration = ReadExpiration(ref buffer);
|
||||
|
||||
imageCacheItem = new ImageCacheItem
|
||||
{
|
||||
|
|
@ -282,20 +268,6 @@ namespace MapControl.Caching
|
|||
return null;
|
||||
}
|
||||
|
||||
private string GetPath(string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Path.Combine(rootDirectory, Path.Combine(key.Split('/', ':', ';', ',')));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("ImageFileCache: Invalid key {0}/{1}: {2}", rootDirectory, key, ex.Message);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task CleanRootDirectory()
|
||||
{
|
||||
foreach (var dir in new DirectoryInfo(rootDirectory).EnumerateDirectories())
|
||||
|
|
@ -349,22 +321,9 @@ namespace MapControl.Caching
|
|||
return deletedFileCount;
|
||||
}
|
||||
|
||||
private static DateTime GetExpiration(ref byte[] buffer)
|
||||
{
|
||||
DateTime expiration = DateTime.Today;
|
||||
|
||||
if (buffer.Length > 16 && Encoding.ASCII.GetString(buffer, buffer.Length - 16, 8) == expiresTag)
|
||||
{
|
||||
expiration = new DateTime(BitConverter.ToInt64(buffer, buffer.Length - 8), DateTimeKind.Utc);
|
||||
Array.Resize(ref buffer, buffer.Length - 16);
|
||||
}
|
||||
|
||||
return expiration;
|
||||
}
|
||||
|
||||
private static async Task<DateTime> ReadExpirationAsync(FileInfo file)
|
||||
{
|
||||
DateTime expiration = DateTime.Today;
|
||||
DateTime? expiration = null;
|
||||
|
||||
if (file.Length > 16)
|
||||
{
|
||||
|
|
@ -374,15 +333,14 @@ namespace MapControl.Caching
|
|||
{
|
||||
stream.Seek(-16, SeekOrigin.End);
|
||||
|
||||
if (await stream.ReadAsync(buffer, 0, 16).ConfigureAwait(false) == 16 &&
|
||||
Encoding.ASCII.GetString(buffer, 0, 8) == expiresTag)
|
||||
if (await stream.ReadAsync(buffer, 0, 16).ConfigureAwait(false) == 16)
|
||||
{
|
||||
expiration = new DateTime(BitConverter.ToInt64(buffer, 8), DateTimeKind.Utc);
|
||||
expiration = ReadExpiration(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expiration;
|
||||
return expiration ?? DateTime.Today;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,11 @@
|
|||
// © 2021 Clemens Fischer
|
||||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using MapControl.Caching;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Caching;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
using MapControl.Caching;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
|
|
@ -39,7 +38,7 @@ namespace MapControl
|
|||
|
||||
private static async Task LoadCachedTileAsync(Tile tile, Uri uri, string cacheKey)
|
||||
{
|
||||
var cacheItem = await GetCacheAsync(cacheKey).ConfigureAwait(false);
|
||||
var cacheItem = Cache.Get(cacheKey) as ImageCacheItem;
|
||||
var buffer = cacheItem?.Buffer;
|
||||
|
||||
if (cacheItem == null || cacheItem.Expiration < DateTime.UtcNow)
|
||||
|
|
@ -48,9 +47,15 @@ namespace MapControl
|
|||
|
||||
if (response != null) // download succeeded
|
||||
{
|
||||
buffer = response.Buffer; // may be null or empty when no tile available, but still be cached
|
||||
buffer = response.Buffer;
|
||||
|
||||
await SetCacheAsync(cacheKey, buffer, GetExpiration(response.MaxAge)).ConfigureAwait(false);
|
||||
cacheItem = new ImageCacheItem
|
||||
{
|
||||
Buffer = buffer, // may be null or empty when no tile available, but still be cached
|
||||
Expiration = GetExpiration(response.MaxAge)
|
||||
};
|
||||
|
||||
Cache.Set(cacheKey, cacheItem, new CacheItemPolicy { AbsoluteExpiration = cacheItem.Expiration });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -68,26 +73,5 @@ namespace MapControl
|
|||
|
||||
await tile.Image.Dispatcher.InvokeAsync(() => tile.SetImage(image));
|
||||
}
|
||||
|
||||
private static Task<ImageCacheItem> GetCacheAsync(string cacheKey)
|
||||
{
|
||||
return Task.Run(() => Cache.Get(cacheKey) as ImageCacheItem);
|
||||
}
|
||||
|
||||
private static Task SetCacheAsync(string cacheKey, byte[] buffer, DateTime expiration)
|
||||
{
|
||||
var imageCacheItem = new ImageCacheItem
|
||||
{
|
||||
Buffer = buffer,
|
||||
Expiration = expiration
|
||||
};
|
||||
|
||||
var cacheItemPolicy = new CacheItemPolicy
|
||||
{
|
||||
AbsoluteExpiration = expiration
|
||||
};
|
||||
|
||||
return Task.Run(() => Cache.Set(cacheKey, imageCacheItem, cacheItemPolicy));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue