Unified ImageFileCache implementations

This commit is contained in:
Clemens 2021-06-30 00:04:44 +02:00
parent a9475f79fe
commit 903faa9fb8
2 changed files with 47 additions and 30 deletions

View file

@ -5,6 +5,8 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Windows.Storage; using Windows.Storage;
using Windows.Storage.Streams; using Windows.Storage.Streams;
@ -13,6 +15,8 @@ namespace MapControl.Caching
{ {
public class ImageFileCache : IImageCache public class ImageFileCache : IImageCache
{ {
private const string expiresTag = "EXPIRES:";
private readonly string folderPath; private readonly string folderPath;
public ImageFileCache(StorageFolder folder) public ImageFileCache(StorageFolder folder)
@ -28,6 +32,7 @@ namespace MapControl.Caching
public async Task<ImageCacheItem> GetAsync(string key) public async Task<ImageCacheItem> GetAsync(string key)
{ {
ImageCacheItem imageCacheItem = null;
string path; string path;
try try
@ -37,7 +42,7 @@ namespace MapControl.Caching
catch (Exception ex) catch (Exception ex)
{ {
Debug.WriteLine("ImageFileCache: Invalid key {0}: {1}", key, ex.Message); Debug.WriteLine("ImageFileCache: Invalid key {0}: {1}", key, ex.Message);
return null; return imageCacheItem;
} }
var folder = await StorageFolder.GetFolderFromPathAsync(folderPath); var folder = await StorageFolder.GetFolderFromPathAsync(folderPath);
@ -46,28 +51,32 @@ namespace MapControl.Caching
if (item != null && item.IsOfType(StorageItemTypes.File)) if (item != null && item.IsOfType(StorageItemTypes.File))
{ {
var file = (StorageFile)item; var file = (StorageFile)item;
//Debug.WriteLine("ImageFileCache: Reading " + file.Path);
try try
{ {
return new ImageCacheItem var buffer = (await FileIO.ReadBufferAsync(file)).ToArray();
var expiration = GetExpiration(ref buffer);
imageCacheItem = new ImageCacheItem
{ {
Buffer = await FileIO.ReadBufferAsync(file), Buffer = buffer.AsBuffer(),
Expiration = (await file.Properties.GetImagePropertiesAsync()).DateTaken.UtcDateTime Expiration = expiration
}; };
//Debug.WriteLine("ImageFileCache: Read {0}, Expires {1}", file.Path, expiration.ToLocalTime());
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.WriteLine("ImageFileCache: Reading {0}: {1}", file.Path, ex.Message); Debug.WriteLine("ImageFileCache: Failed reading {0}: {1}", file.Path, ex.Message);
} }
} }
return null; return imageCacheItem;
} }
public async Task SetAsync(string key, IBuffer buffer, DateTime expiration) public async Task SetAsync(string key, IBuffer buffer, DateTime expiration)
{ {
if (buffer != null && buffer.Length > 0) // do not cache a no-tile entry if (buffer != null && buffer.Length > 0)
{ {
var folders = GetPathElements(key); var folders = GetPathElements(key);
@ -81,19 +90,19 @@ namespace MapControl.Caching
} }
var file = await folder.CreateFileAsync(folders[folders.Length - 1], CreationCollisionOption.ReplaceExisting); var file = await folder.CreateFileAsync(folders[folders.Length - 1], CreationCollisionOption.ReplaceExisting);
//Debug.WriteLine("ImageFileCache: Writing {0}, Expires {1}", file.Path, expiration.ToLocalTime());
await FileIO.WriteBufferAsync(file, buffer); //Debug.WriteLine("ImageFileCache: Write {0}, Expires {1}", file.Path, expiration.ToLocalTime());
// Store expiration date in ImageProperties.DateTaken using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
var properties = await file.Properties.GetImagePropertiesAsync(); {
properties.DateTaken = expiration; await stream.WriteAsync(buffer);
await stream.WriteAsync(Encoding.ASCII.GetBytes(expiresTag).AsBuffer());
await properties.SavePropertiesAsync(); await stream.WriteAsync(BitConverter.GetBytes(expiration.Ticks).AsBuffer());
}
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.WriteLine("ImageFileCache: Writing {0}: {1}", Path.Combine(folderPath, Path.Combine(folders)), ex.Message); Debug.WriteLine("ImageFileCache: Failed writing {0}: {1}", Path.Combine(folderPath, Path.Combine(folders)), ex.Message);
} }
} }
} }
@ -102,5 +111,18 @@ namespace MapControl.Caching
{ {
return key.Split('\\', '/', ',', ':', ';'); return key.Split('\\', '/', ',', ':', ';');
} }
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;
}
} }
} }

View file

@ -21,7 +21,7 @@ namespace MapControl.Caching
/// </summary> /// </summary>
public class ImageFileCache : ObjectCache public class ImageFileCache : ObjectCache
{ {
private const string ExpiresTag = "EXPIRES:"; private const string expiresTag = "EXPIRES:";
private static readonly FileSystemAccessRule fullControlRule = new FileSystemAccessRule( private static readonly FileSystemAccessRule fullControlRule = new FileSystemAccessRule(
new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null), new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null),
@ -125,7 +125,7 @@ namespace MapControl.Caching
memoryCache.Set(key, imageCacheItem, new CacheItemPolicy { AbsoluteExpiration = expiration }); memoryCache.Set(key, imageCacheItem, new CacheItemPolicy { AbsoluteExpiration = expiration });
//Debug.WriteLine("ImageFileCache: Reading {0}, Expires {1}", path, imageCacheItem.Expiration.ToLocalTime()); //Debug.WriteLine("ImageFileCache: Read {0}, Expires {1}", path, imageCacheItem.Expiration.ToLocalTime());
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -176,14 +176,15 @@ namespace MapControl.Caching
{ {
try try
{ {
//Debug.WriteLine("ImageFileCache: Writing {0}, Expires {1}", path, imageCacheItem.Expiration.ToLocalTime()); //Debug.WriteLine("ImageFileCache: Write {0}, Expires {1}", path, imageCacheItem.Expiration.ToLocalTime());
Directory.CreateDirectory(Path.GetDirectoryName(path)); Directory.CreateDirectory(Path.GetDirectoryName(path));
using (var stream = File.Create(path)) using (var stream = File.Create(path))
{ {
stream.Write(imageCacheItem.Buffer, 0, imageCacheItem.Buffer.Length); stream.Write(imageCacheItem.Buffer, 0, imageCacheItem.Buffer.Length);
SetExpiration(stream, imageCacheItem.Expiration); stream.Write(Encoding.ASCII.GetBytes(expiresTag), 0, 8);
stream.Write(BitConverter.GetBytes(imageCacheItem.Expiration.Ticks), 0, 8);
} }
var fileInfo = new FileInfo(path); var fileInfo = new FileInfo(path);
@ -348,17 +349,11 @@ namespace MapControl.Caching
return deletedFileCount; return deletedFileCount;
} }
private static void SetExpiration(Stream stream, DateTime expiration)
{
stream.Write(Encoding.ASCII.GetBytes(ExpiresTag), 0, 8);
stream.Write(BitConverter.GetBytes(expiration.Ticks), 0, 8);
}
private static DateTime GetExpiration(ref byte[] buffer) private static DateTime GetExpiration(ref byte[] buffer)
{ {
DateTime expiration = DateTime.MaxValue; DateTime expiration = DateTime.Today;
if (buffer.Length > 16 && Encoding.ASCII.GetString(buffer, buffer.Length - 16, 8) == ExpiresTag) if (buffer.Length > 16 && Encoding.ASCII.GetString(buffer, buffer.Length - 16, 8) == expiresTag)
{ {
expiration = new DateTime(BitConverter.ToInt64(buffer, buffer.Length - 8), DateTimeKind.Utc); expiration = new DateTime(BitConverter.ToInt64(buffer, buffer.Length - 8), DateTimeKind.Utc);
Array.Resize(ref buffer, buffer.Length - 16); Array.Resize(ref buffer, buffer.Length - 16);
@ -369,7 +364,7 @@ namespace MapControl.Caching
private static async Task<DateTime> ReadExpirationAsync(FileInfo file) private static async Task<DateTime> ReadExpirationAsync(FileInfo file)
{ {
DateTime expiration = DateTime.MaxValue; DateTime expiration = DateTime.Today;
if (file.Length > 16) if (file.Length > 16)
{ {
@ -380,7 +375,7 @@ namespace MapControl.Caching
stream.Seek(-16, SeekOrigin.End); stream.Seek(-16, SeekOrigin.End);
if (await stream.ReadAsync(buffer, 0, 16).ConfigureAwait(false) == 16 && if (await stream.ReadAsync(buffer, 0, 16).ConfigureAwait(false) == 16 &&
Encoding.ASCII.GetString(buffer, 0, 8) == ExpiresTag) Encoding.ASCII.GetString(buffer, 0, 8) == expiresTag)
{ {
expiration = new DateTime(BitConverter.ToInt64(buffer, 8), DateTimeKind.Utc); expiration = new DateTime(BitConverter.ToInt64(buffer, 8), DateTimeKind.Utc);
} }