这个例子展示了使用LEADTOOLS文档库实现Azure Redis缓存和存储Blobs。
这是一个可以在生产环境中使用的成熟示例。它使用Azure存储Blobs来存储二进制数据,而不是直接存储到缓存中。如果文档可以从多个服务器访问,则这种方法非常适合。有关将大型二进制数据直接保存到缓存的示例,请参阅Azure Redis缓存示例.
这个示例代码需要以下NuGet包:
在文档服务源代码中,替换其中的代码ServiceHelper。CreateCache:
//获取Redis Cache数据库对象var配置=“your-cache-url.redis.cache.windows.net,密码=你的密码”;varconfigurationOptions = configurationOptions . parse(配置);ConnectionMultiplexer connection = ConnectionMultiplexer. connect (configurationOptions);IDatabase redisDatabase = connection.GetDatabase();//获取用于存储二进制数据的Blob存储容器CloudConfigurationManager.GetSetting(CloudConfigurationManager.GetSetting)“StorageConnectionString”));CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();//容器名可以是任何东西,我们选择document-service来实现字符串containerName =“文档服务”;CloudBlobContainer container = blobClient.GetContainerReference(containerName);//可选:如果容器不存在,则返回的任务。运行(async () => await container.CreateIfNotExistsAsync()).Wait();//创建我们的LEADTOOLS ObjectCache包装器_cache =新RedisWithBlobsObjectCache(redisDatabase, blobClient, containerName);
RedisWithBlobsObjectCache.cs
使用Leadtools;使用Leadtools.Caching;使用Leadtools.Codecs;使用Leadtools.Svg;使用Microsoft.WindowsAzure.Storage.Blob;使用Newtonsoft.Json;使用StackExchange.Redis;使用系统;使用System.Collections.Generic;使用System.Diagnostics;使用先;使用System.Threading.Tasks;名称空间MyNamespace{///<摘要>///包装一个Redis缓存IDatabase对象,与LEADTOOLS文档库一起使用。///> < /总结///<评论>///<对位>///此实现增加了将二进制数据存储到Cloud Blob存储中的支持。///用户指定容器名,二进制数据存储在block blob中,names = "regionName/key"。///< / para >///< /评论>公共类RedisWithBlobsObjectCache: ObjectCache{私人RedisWithBlobsObjectCache() {}///<摘要>///从Redis缓存数据库对象初始化一个LEADTOOLS对象缓存包装器。///> < /总结///已完全初始化的Redis数据库对象/// .<参数名称="blobClient">Azure blob客户端准备使用公共RedisWithBlobsObjectCache (StackExchange.Redis。数据库缓存,CloudBlobClient,字符串containerName =零){这.Cache = cache;这.BlobClient = blobClient;//要使用的Blob存储容器名称如果(containerName = =零)这.ContainerName = Guid.NewGuid().ToString().Replace(“-”,”“);其他的这.ContainerName = containerName;}///<摘要>///正在使用的Redis缓存数据库对象。///> < /总结公共idatabase缓存{得到;私人集;}///<摘要>///正在使用的Azure Blob客户端对象。///> < /总结公共CloudBlobClient BlobClient {得到;私人集;}///<摘要>///正在使用的Azure Blob容器名称。///> < /总结公共字符串ContainerName {得到;私人集;}// --------------------------------------------------------------------------------------//这些成员必须由我们的类实现,并由Document工具箱调用// --------------------------------------------------------------------------------------公共覆盖字符串名字{得到{返回“Redis与Blobs对象缓存”;}}公共覆盖CacheSerializationMode PolicySerializationMode{得到{// Redis不使用这个,所以我们假设它是二进制的返回CacheSerializationMode.Binary;}集{扔新NotSupportedException(由于);}}公共覆盖CacheSerializationMode DataSerializationMode{得到{//二进制意味着我们将自己序列化返回CacheSerializationMode.Binary;}集{扔新NotSupportedException(由于);}}公共覆盖DefaultCacheCapabilities DefaultCacheCapabilities{得到{//我们支持序列化:这意味着,工具箱可以给我们发送“胖”的。net对象//我们将序列化它们而不改变原始引用返回DefaultCacheCapabilities.Serialization;}}公共覆盖CacheItemAddOrGetExisting (CacheItem item, CacheItemPolicy policy) {//添加缓存项时调用的方法。//必须返回旧值//解析密钥。记住,我们没有区域varresolvedKey = ResolveCacheKey(项目。RegionName item.Key);CacheItemoldItem =零; //获取旧值(如果有的话)如果(这.Cache.KeyExists (resolvedKey)){RedisValue existingValue =这.Cache.StringGet (resolvedKey);varoldValue = GetFromCacheAndBlobStorage(existingValue, item. value)RegionName item.Key); oldItem =新CacheItem < T >(项目。Key, (T)oldValue, item.RegionName);}//添加新值AddToCacheAndBlobStorage(项目、政策);//返回旧项目返回oldItem;}公共覆盖CacheItem < T > GetCacheItem < T > (字符串键,字符串regionName){//如果我们有一个带有此键的项,则返回它。否则,返回nullvarresolvedKey = ResolveCacheKey(regionName, key);CacheItemitem =零; 如果(这.Cache.KeyExists (resolvedKey)){RedisValue value =这.Cache.StringGet (resolvedKey);varitemValue = GetFromCacheAndBlobStorage(value, regionName, key); 项=新CacheItem(key, (T)itemValue, regionName); }返回项;}公共覆盖保龄球包含(字符串键,字符串regionName){//检查键是否在字典中varresolvedKey = ResolveCacheKey(regionName, key);var存在=这.Cache.KeyExists (resolvedKey);返回存在;}公共覆盖保龄球UpdateCacheItem < T > (CacheItem < T >项){//更新项目如果(项目= =零)扔新ArgumentNullException (“项目”);varresolvedKey = ResolveCacheKey(项目。RegionName item.Key);var存在=这.Cache.KeyExists (resolvedKey);如果(存在){AddToCacheAndBlobStorage(项目,零);}返回存在;}公共覆盖T删除< T > (字符串键,字符串regionName){//删除找到的值,返回旧值T existingValue =默认的(T);varresolvedKey = ResolveCacheKey(regionName, key);如果(这.Cache.KeyExists (resolvedKey)){RedisValue value =这.Cache.StringGet (resolvedKey);existingValue = (T)GetFromCacheAndBlobStorage(value, regionName, key); }//删除DeleteItem(关键,regionName);返回existingValue;}公共覆盖无效DeleteItem (字符串键,字符串regionName){//如果找到,删除DeleteFromCacheAndBlobStorage (regionName、关键);}公共覆盖无效UpdatePolicy (字符串key, CacheItemPolicy策略字符串regionName){// Redis缓存不允许我们更新一个项目的过期策略。}私人静态字符串ResolveCacheKey (字符串regionName,字符串键){//两个都必须是非空字符串如果(字符串.IsNullOrEmpty (regionName))扔新InvalidOperationException (“地区名称必须是非空字符串”);如果(字符串.IsNullOrEmpty(关键))扔新InvalidOperationException ("区域键名必须是非空字符串");// regionName可能不是唯一的,key也可能不是唯一的,但是把它们结合起来,我们就保证了一个唯一的key返回regionName +“-”+关键;}公共静态字符串ResolveBlobReference (字符串regionName,字符串键){//获取blob引用返回regionName +“/”+关键;}私人无效AddToCacheAndBlobStorage(CacheItem item, CacheItemPolicy policy) {//从项目数据中获取Redis值字节[] blob =零;保龄球hasBlob =假;字符串json =零;vartypeOfT =typeof(T);如果(typeOfT = =typeof(RasterImage)){blob = ImageToBlob(项目。价值作为RasterImage);hasBlob =真正的;}其他的如果(typeOfT = =typeof(SvgDocument)){blob = SvgToBlob(项目。价值作为SvgDocument);hasBlob =真正的;}其他的如果(typeOfT = =typeof(字节[])){Blob =项目。价值作为字节[];hasBlob =真正的;}其他的{// JSON序列化json = JsonConvert.SerializeObject(item.Value);hasBlob =假;}//如果使用滑动过期,则将其设置为绝对值时间间隔?到期=零;如果(政策! =零){varexpiryDate = policy.AbsoluteExpiration;如果(政策。> timespan . 0){expiryDate = DateTime.UtcNow.Add(policy.SlidingExpiration);}//现在,我们有一个日期,将其转换为从现在开始的时间跨度(所有UTC)期满=过期日期。减去(DateTime.UtcNow);}varresolvedKey = ResolveCacheKey(项目。RegionName item.Key);//设置缓存项的值如果(hasBlob){这.Cache。StringSet(resolvedKey, 1,到期);如果(blob ! =零){varblobReference = ResolveBlobReference(项目。RegionName item.Key);CloudBlobContainer容器=这.BlobClient.GetContainerReference (这.ContainerName);CloudBlockBlob blockBlob = container.GetBlockBlobReference(blobReference);的任务。执行(async () => await blockBlob。UploadFromByteArrayAsync(blob, 0, blob. length)).Wait();}}其他的{这.Cache。StringSet(resolvedKey, json,到期);}}私人对象GetFromCacheAndBlobStorage < T > (RedisValue价值,字符串regionName,字符串键){vartypeOfT =typeof(T);对象结果=零;如果(typeOfT = =typeof(光栅图像)|| typeOfT ==typeof(SvgDocument) || typeOfT ==typeof(字节[])){//读取blobCloudBlobContainer容器=这.BlobClient.GetContainerReference (这.ContainerName);CloudBlockBlob blockBlob =容器。GetBlockBlobReference (ResolveBlobReference (regionName键));保龄球exists =任务。Run(async () => await blockBlob.ExistsAsync()).Result;如果(存在)的任务。Run(async () => await blockBlob.FetchAttributesAsync()).Wait();字节[] blob =零;varlength = blockBlob.Properties.Length;如果(长度!= -1){blob =新字节(长度);如果(长度> 0)的任务。执行(async () => await blockBlob。0) DownloadToByteArrayAsync (blob) .Wait ();}如果(typeOfT = =typeof(RasterImage)){result = ImageFromBlob(blob);}其他的如果(typeOfT = =typeof(SvgDocument)){result = SvgFromBlob(blob);}其他的{结果= (字节[])团;}}其他的{// JSON反序列化它result = JsonConvert.DeserializeObject(value); }返回结果;}私人无效DeleteFromCacheAndBlobStorage (字符串regionName,字符串键){varresolvedKey = ResolveCacheKey(regionName, key);如果(这.Cache.KeyExists (resolvedKey))这.Cache.KeyDelete (resolvedKey);CloudBlobContainer容器=这.BlobClient.GetContainerReference (这.ContainerName);CloudBlockBlob blockBlob =容器。GetBlockBlobReference (ResolveBlobReference (regionName键));的任务。Run(async () => await blockBlob.DeleteIfExistsAsync()).Wait();}// Helper方法将RasterImage或SvgDocument对象从/转换为字节[]私人静态字节[] ImageToBlob(RasterImage image){如果(图片= =零)返回零;//以PNG格式保存到内存流中,使用字节[]数据使用(varrasterCodecs =新RasterCodecs ()){使用(var女士=新MemoryStream ()){rasterCodecs。Save(image, ms, RasterImageFormat.Png, 0);返回ms.GetBuffer ();}}}私人静态RasterImage ImageFromBlob (字节[] blob){如果(blob = =零| | blob。长度== 0)返回零;//加载一个光栅图像,使用字节[]数据使用(varrasterCodecs =新RasterCodecs ()){使用(var女士=新MemoryStream (blob)){返回rasterCodecs。负载(ms, 1);}}}私人静态字节[] SvgToBlob(SvgDocument svg){如果(svg = =零)返回零;使用(var女士=新MemoryStream ()){svg。SaveToStream(女士,零);返回ms.GetBuffer ();}}私人静态SvgDocument SvgFromBlob (字节[] blob){如果(blob = =零| | blob。长度== 0)返回零;返回SvgDocument。LoadFromMemory(blob, 0, blob。长度,零);}////这些成员必须被我们的类覆盖,但不会被Document工具箱调用//抛出一个不支持的异常////这是默认区域支持。我们没有公共覆盖对象这[字符串例子){得到{扔新NotSupportedException(由于);}集{扔新NotSupportedException(由于);}}//一次性删除一个区域我们对此不支持//注意:只有当我们有DefaultCacheCapabilities.CacheRegions时才会被调用。因为我们不这样做,所以调用者负责//调用DeleteAll传递区域的所有项目(依次调用每个项目的DeleteItem)公共覆盖无效DeleteRegion (字符串regionName){扔新NotSupportedException(由于);}//开始添加外部资源我们对此不支持//注意:只有当我们有DefaultCacheCapabilities时才会调用。ExternalResources公共覆盖Uri BeginAddExternalResource (字符串键,字符串regionName,保龄球读写){扔新NotSupportedException(由于);}//结束添加外部资源。我们对此不支持//注意:只有当我们有DefaultCacheCapabilities时才会调用。ExternalResources公共覆盖无效EndAddExternalResource < T > (保龄球提交,字符串key, T值,CacheItemPolicy策略,字符串regionName){扔新NotSupportedException(由于);}//获取项目外部资源。我们对此不支持//注意:只有当我们有DefaultCacheCapabilities时才会调用。ExternalResources公共覆盖Uri GetItemExternalResource (字符串键,字符串regionName,保龄球读写){扔新NotSupportedException(由于);}//删除项目外部资源。我们对此不支持//注意:只有当我们有DefaultCacheCapabilities时才会调用。ExternalResources公共覆盖无效RemoveItemExternalResource (字符串键,字符串regionName){扔新NotSupportedException(由于);}//获取项目虚拟目录路径我们对此不支持//注意:只有当我们有DefaultCacheCapabilities时才会调用。VirtualDirectory公共覆盖Uri GetItemVirtualDirectoryUrl (字符串键,字符串regionName){扔新NotSupportedException(由于);}//获取缓存中的条目数。我们对此不支持公共覆盖长GetCount (字符串regionName){扔新NotSupportedException(由于);}/ /统计数据。我们对此不支持公共覆盖CacheStatistics GetStatistics (){扔新NotSupportedException(由于);}/ /统计数据。我们对此不支持公共覆盖CacheStatistics GetStatistics (字符串键,字符串regionName){扔新NotSupportedException(由于);}//获取所有的值。我们对此不支持公共覆盖IDictionary <字符串,对象> getvalue (IEnumerable <字符串>键,字符串regionName){扔新NotSupportedException(由于);}//项目的枚举。我们对此不支持受保护的覆盖IEnumerator < KeyValuePair <字符串,对象> > GetEnumerator (){扔新NotSupportedException(由于);}//键的枚举我们对此不支持公共覆盖无效EnumerateKeys (字符串区域,EnumerateCacheEntriesCallback{扔新NotSupportedException(由于);}//区域的枚举。我们对此不支持公共覆盖无效EnumerateRegions (EnumerateCacheEntriesCallback回调){扔新NotSupportedException(由于);}}}