内存缓存和本地文件缓存技术
如果每个瓦片图每次打开都要重新加载,会比较浪费资源,而且如果网络不好,甚至要等很久,于是可以使用内存缓存,每次已经加载的瓦片图,第二次再加载之前,先看看内存中有没有,这就是内存缓存技术。但是软件已关闭,内存数据全部清空了,下次再打开,又要重新加载,比较浪费资源,于是可以使用本地文件缓存技术,也就是离线地图。
/// <summary>
/// 缓存管理
/// </summary>
internal class CacheManager : IDisposable
{/// <summary>/// 默认缓存一个G/// </summary>public int MemoryMaxCacheSize = 1000000;/// <summary>/// 当前已经使用的内存缓存大小/// </summary>public long CurrentMemoryCacheSize = 0;/// <summary>/// 缓存/// </summary>private HashSet<string> LocalCache = new HashSet<string>();/// <summary>/// 内存缓存/// </summary>private IntPtr MemoryCache = IntPtr.Zero;/// <summary>/// 是否已经释放/// </summary>private bool isDisposed = false;/// <summary>/// 内存缓存数据缓存区域/// </summary>private Dictionary<string, MemoryRange> MemoryCacheDataRange = new Dictionary<string, MemoryRange>();/// <summary>/// 缓存文件夹/// </summary>public string CacheFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "map_caches");/// <summary>/// 是否开启本地文件缓存/// </summary>private bool IsOpenLocalCache = true;/// <summary>/// 是否开启内存缓存/// </summary>private bool IsOpenMemoryCache = true;/// <summary>/// 内存缓存是否已经满了/// </summary>private bool IsMemoryFull = false;/// <summary>/// 锁/// </summary>private object MemoryLocked = new object();private object LocalLocked = new object();/// <summary>/// 构造函数/// </summary>/// <param name="size">内存缓存大小</param>/// <param name="unit">内存缓存单位</param>/// <param name="isOpenLocalCache">是否开启文件缓存</param>/// <param name="isOpenMemoryCache">是否开启内存缓存</param>public CacheManager(int size, MemorySizeUnit unit, bool isOpenLocalCache, bool isOpenMemoryCache){switch (unit){case MemorySizeUnit.GB:MemoryMaxCacheSize = size * 1024 * 1024 * 1024;break;case MemorySizeUnit.MB:MemoryMaxCacheSize = size * 1024 * 1024;break;case MemorySizeUnit.KB:MemoryMaxCacheSize = size * 1024;break;default:break;}IsOpenLocalCache = isOpenLocalCache;IsOpenMemoryCache = isOpenMemoryCache;MemoryCache = Marshal.AllocHGlobal(MemoryMaxCacheSize);InitCacheMode();}/// <summary>/// 析构函数/// </summary>~CacheManager(){Dispose(false);}/// <summary>/// 初始化缓存模型/// </summary>public void InitCacheMode(){if (IsOpenLocalCache){Debug.WriteLine("地图开启缓存模式...");// 如果开启缓存模式if (!Directory.Exists(CacheFolder)){Directory.CreateDirectory(CacheFolder);}var dirs = Directory.GetDirectories(CacheFolder);foreach (var dir in dirs){foreach (var file in Directory.GetFiles(dir)){LocalCache.Add(file);}}}}/// <summary>/// 读取或者添加一个内存缓存/// </summary>/// <param name="name"></param>/// <param name="memoryData"></param>/// <param name="size"></param>/// <returns></returns>public unsafe (MemoryRange, bool) ReadOrSetFromMemoryCache(string name, System.Drawing.Bitmap map = null){lock (MemoryLocked){if (map == null){MemoryRange memory = null;MemoryCacheDataRange.TryGetValue(name, out memory);return (memory, true);}else{if(IsMemoryFull){return (null, false);}var size = map.Width * map.Height * 3;if(CurrentMemoryCacheSize + size > MemoryMaxCacheSize){IsMemoryFull = true;return (null, false);}var range = new MemoryRange(CurrentMemoryCacheSize, CurrentMemoryCacheSize + size, MemoryCache + (int)CurrentMemoryCacheSize);var mapData = map.LockBits(new System.Drawing.Rectangle(new System.Drawing.Point(), map.Size), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);Buffer.MemoryCopy(mapData.Scan0.ToPointer(), range.Address.ToPointer(), range.Length, (int)range.Length);map.UnlockBits(mapData);MemoryCacheDataRange.Add(name, range);CurrentMemoryCacheSize += size;return (range, true);}}}/// <summary>/// 读取或者添加一个文件缓存/// </summary>/// <param name="name"></param>/// <param name="memoryData"></param>/// <param name="size"></param>/// <returns></returns>public bool ReadOrSetFromLocalCache(string name, bool isGet){lock (LocalLocked){if (isGet){if (LocalCache.Contains(name)){return true;}return false;}else{// 如果已经存在本地了 则不需要重新添加if (LocalCache.Contains(name))return false;LocalCache.Add(name);return true;}}}/// <summary>/// 获取缓存文件路径/// </summary>/// <param name="block"></param>/// <returns></returns>private string GetCacheFilePath(TitleBlock titleBlock){var dir = Path.Combine(CacheFolder, titleBlock.Level.ToString());if (Directory.Exists(dir) is false) Directory.CreateDirectory(dir);var fileName = string.Format("{0}\\{1}", dir, titleBlock.ToCacheFileName());return fileName;}/// <summary>/// 读取数据/// </summary>/// <param name="block"></param>/// <returns></returns>public async Task<MemoryRange> ReadTitleData(TitleBlock block){MemoryRange address = null;string memoryCacheName = block.ToCacheKey(); //内存缓存名称string localCacheName = GetCacheFilePath(block); //本地文件缓存名称if (IsOpenMemoryCache){address = ReadOrSetFromMemoryCache(memoryCacheName).Item1;}// 如果从内存中直接读取出来则直接返回if (address != null) return address;System.Drawing.Bitmap bitmap = null;// 如果 开启了文本本地缓存,并且文件确实在本地有缓存if (IsOpenLocalCache && ReadOrSetFromLocalCache(localCacheName, true)){// 从文件中读取数据bitmap = ReadDataFromLocalFile(localCacheName);}// 如果文件中不存在 进行下载if(bitmap == null){bitmap = await ReadDatasFromHttp(block.Url, localCacheName);}// 存入内存if (bitmap != null && IsOpenMemoryCache){// 存入内存var res = ReadOrSetFromMemoryCache(memoryCacheName, bitmap);// 如果保存成功了if (res.Item2){address = res.Item1;bitmap.Dispose();}else{address = new MemoryRange(0, bitmap.Width * 3 * bitmap.Height, IntPtr.Zero) { Bitmap = bitmap };}}// 如果获取到图片并且不需要内存缓存则直接使用else if (bitmap != null && !IsOpenMemoryCache && address == null){address = new MemoryRange(0, bitmap.Width * 3 * bitmap.Height, IntPtr.Zero) { Bitmap = bitmap };}return address;}/// <summary>/// 转换位图/// </summary>/// <param name="jpegDatas"></param>/// <param name="isSaveToLocal"></param>/// <param name="localPath"></param>/// <returns></returns>private System.Drawing.Bitmap ConvertToBmpDatas(MemoryStream jpegDatas, bool isSaveToLocal, string localPath){System.Drawing.Bitmap jpeg = System.Drawing.Image.FromStream(jpegDatas) as System.Drawing.Bitmap;if (isSaveToLocal){jpeg.Save(localPath, System.Drawing.Imaging.ImageFormat.Bmp);}return jpeg;}/// <summary>/// 从http中读取数据/// </summary>/// <param name="url"></param>/// <param name="localCacheName"></param>/// <returns></returns>private async Task<System.Drawing.Bitmap> ReadDatasFromHttp(Uri url, string localCacheName){System.Drawing.Bitmap bitmap = null;int tryTimes = 10;using (HttpClient Client = new HttpClient()){while (tryTimes-- > 0 && bitmap == null){try{var res = await Client.GetAsync(url);if (res.StatusCode == System.Net.HttpStatusCode.OK){var stream = await res.Content.ReadAsStreamAsync() as MemoryStream;// 读取数据并且检测是否保存到本地文件bitmap = ConvertToBmpDatas(stream, IsOpenLocalCache, localCacheName);// 添加一个本地的文件缓存信息if (IsOpenLocalCache){ReadOrSetFromLocalCache(localCacheName, false);}if (bitmap != null) return bitmap;}}catch (Exception ex) { }}}return bitmap;}/// <summary>/// 从本地文件缓存中加载数据/// </summary>/// <param name="localCacheName">本地缓存文件名称</param>/// <returns></returns>private Bitmap ReadDataFromLocalFile(string localCacheName){try{var bitmap = new System.Drawing.Bitmap(localCacheName);if (bitmap == null || bitmap.Width == 0 || bitmap.Height == 0){if (bitmap != null) bitmap.Dispose();bitmap = null;}return bitmap;}catch (Exception ex){return null;}}/// <summary>/// 释放/// </summary>public void Dispose(){Dispose(true);}public void Dispose(bool dispose){if (isDisposed){return;}if (dispose){// 释放托管的 IDispose 对象}// 释放非托管内容Marshal.FreeHGlobal(MemoryCache);isDisposed = true;}
}
获取瓦片的时候
/// <summary>
/// 读取数据
/// </summary>
/// <returns></returns>
public Task<MemoryRange> ReadStream() => CacheManager.ReadTitleData(this);