diff --git a/MapControl/Shared/ImageFileCache.cs b/MapControl/Shared/ImageFileCache.cs
new file mode 100644
index 00000000..a1a376d9
--- /dev/null
+++ b/MapControl/Shared/ImageFileCache.cs
@@ -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
+{
+ ///
+ /// Image Cache implementation based on local image files.
+ /// The only valid data type for cached values is MapControl.ImageCacheItem.
+ ///
+ 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;
+ }
+ }
+}
diff --git a/MapControl/UWP/ImageFileCache.UWP.cs b/MapControl/UWP/ImageFileCache.UWP.cs
index 8ff93582..bc28b68f 100644
--- a/MapControl/UWP/ImageFileCache.UWP.cs
+++ b/MapControl/UWP/ImageFileCache.UWP.cs
@@ -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 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;
- }
}
}
diff --git a/MapControl/UWP/MapControl.UWP.csproj b/MapControl/UWP/MapControl.UWP.csproj
index b73d5592..78727012 100644
--- a/MapControl/UWP/MapControl.UWP.csproj
+++ b/MapControl/UWP/MapControl.UWP.csproj
@@ -77,6 +77,9 @@
HyperlinkText.cs
+
+ ImageFileCache.cs
+
ImageLoader.cs
diff --git a/MapControl/WPF/ImageFileCache.WPF.cs b/MapControl/WPF/ImageFileCache.WPF.cs
index e9ec997a..d84462b2 100644
--- a/MapControl/WPF/ImageFileCache.WPF.cs
+++ b/MapControl/WPF/ImageFileCache.WPF.cs
@@ -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.
///
- 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 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;
}
}
}
diff --git a/MapControl/WPF/TileImageLoader.WPF.cs b/MapControl/WPF/TileImageLoader.WPF.cs
index d6b121be..02cef0cf 100644
--- a/MapControl/WPF/TileImageLoader.WPF.cs
+++ b/MapControl/WPF/TileImageLoader.WPF.cs
@@ -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 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));
- }
}
}