/// <summary> /// 合并模板和数据字典,生成静态文件,同时放入缓存中。 /// </summary> /// <param name="templateName">模板文件名称</param> /// <param name="dict">数据字典</param> /// <param name="absoluteFilePath">生成文件绝对路径(不包含物理路径)</param> /// <param name="isUseLocalCache">是否只使用本地缓存的方式存放静态文件</param> /// <returns>返回模板页面内容(去掉VM语法之后的结果),文件的内容</returns> private static string MergeAndGenerateFile(string templateName, Dictionary <string, object> dict, string absoluteFilePath, bool isUseLocalCache) { string fileContent = NVelocityBus.MergeVM(templateName, dict); if (!string.IsNullOrWhiteSpace(fileContent)) { if (isUseLocalCache) {//如果页面设置生成静态页面,并且该页面单独设置只使用本地缓存存放静态页面(例如:google爬虫收录的页面,因为量很大并且很集中,不适合放在Redis缓存中。)(一级缓存) UseLocalCache(absoluteFilePath, fileContent, true); } else { //使用本地缓存存放数据(一级缓存)。 UseLocalCache(absoluteFilePath, fileContent, !NVelocityBus.CACHE_REDIS); if (NVelocityBus.CACHE_REDIS) {//如果页面设置生成静态页面,并且网站设置使用Redis缓存存放静态页面 try { //写入Redis缓存(二级缓存)。 //StackExchangeRedisBus.StringSet(absoluteFilePath, ZipHelper.GZipCompress(fileContent), TimeSpan.FromDays(NVelocityBus.CACHE_DAY)); } catch (Exception) {//写入Redis失败 } } } } return(fileContent); }
/// <summary> /// 生成静态文件 /// </summary> /// <param name="absoluteFilePath">生成文件绝对路径(不包含物理路径)</param> /// <param name="fileContent">要保存的文件内容</param> /// <param name="fullFilePath">生成文件文件成功之后,返回文件的物理路径,返回null表示生成文件失败</param> private static void GenerateFile(string absoluteFilePath, string fileContent, out string fullFilePath) { fullFilePath = null; //文件名转码,防止URL参数当文件名过长.wangwei-2014-06-2 absoluteFilePath = System.Web.HttpUtility.UrlDecode(absoluteFilePath); if (!string.IsNullOrEmpty(fileContent)) { fullFilePath = NVelocityBus.GenerateFilePath(absoluteFilePath); bool isGenerateFile = true; char[] invalidFileNameChars = IOHelper.InvalidFileNameChars; string fileName = IOHelper.GetFileNameWithoutExtension(fullFilePath); foreach (char invalidChar in invalidFileNameChars) { if (fileName.Contains(new string(invalidChar, 1))) { isGenerateFile = false; break; } } fileName = null; if (isGenerateFile) { char[] invalidPathChars = IOHelper.InvalidPathChars; string filePath = IOHelper.GetDirectoryName(fullFilePath); foreach (char invalidChar in invalidPathChars) { if (filePath.Contains(new string(invalidChar, 1))) { isGenerateFile = false; break; } } filePath = null; } if (isGenerateFile) { lock (@FileLockObject) { try { IOHelper.GenerateFile(fullFilePath, fileContent); } catch { fullFilePath = null; } } } else { fullFilePath = null; } } }
/// <summary> /// 使用本地缓存 /// </summary> /// <param name="absoluteFilePath">生成文件的绝对路径</param> /// <param name="fileContent">文件内容</param> /// <param name="isGenerateFile">是否生成本地文件</param> private static void UseLocalCache(string absoluteFilePath, string fileContent, bool isGenerateFile) { string fullFilePath = null; if (isGenerateFile) { //如果需要生成本地静态文件 NVelocityBus.GenerateFile(absoluteFilePath, fileContent, out fullFilePath);//生成静态文件 } if (string.IsNullOrEmpty(fullFilePath)) { //如果生成文件失败,也放进缓存中(一级缓存),但是放入缓存的时间设置为绝对过期时间。防止频繁请求服务器端占用更多的资源。 DataCacheBus.Insert(absoluteFilePath, fileContent, DateTime.Now.AddMinutes(CACHE_DATETIME)); } else { //如果生成文件成功,放入缓存并设置相对过期时间同时还加上缓存依赖。(一级缓存) DataCacheBus.Insert(absoluteFilePath, fileContent, TimeSpan.FromMinutes(CACHE_DATETIME), fullFilePath); } }
/// <summary> /// 输出到客户端浏览器 /// </summary> /// <param name="request"></param> /// <param name="response"></param> /// <param name="templateName">Velocity模板文件名称</param> /// <param name="dict">Velocity模板文件所用到的数据字典</param> /// <param name="isGenerateFile">是否生成静态文件</param> /// <param name="isUseLocalCache">是否只使用本地缓存的方式存放静态文件(例如:Google爬虫收录的页面,只需要在本地生成静态文件。)</param> public static void Print(HttpRequest request, HttpResponse response, string templateName, Dictionary <string, object> dict, bool isGenerateFile, bool isUseLocalCache) { string fileContent = null; string absoluteFilePath = null; if (isGenerateFile) { absoluteFilePath = request.RequestContext.HttpContext.Items[NVelocityBus.NVELOCITY_TARGET_FILE_PATH] as string; //改从上下文对象里面获取,2015-11-17。 //当设置生成静态页面时才会生成静态的文件。 fileContent = NVelocityBus.MergeAndGenerateFile(templateName, dict, absoluteFilePath, isUseLocalCache); } else { //没有设置生成静态文件,并且不满足生成条件的,一律按动态页面处理。 fileContent = NVelocityBus.MergeVM(templateName, dict); } if (!string.IsNullOrWhiteSpace(fileContent)) { NVelocityBus.Output(request, response, fileContent, absoluteFilePath, isGenerateFile); } }
public static void BeginRequest(HttpRequest request, HttpResponse response) { Uri filterUri = GetUriWithoutTrace(request, response); int segmentsLength = filterUri.Segments.Length; if (string.IsNullOrWhiteSpace(IOHelper.GetFileExtension(segmentsLength > 0 ? filterUri.Segments[segmentsLength - 1] : filterUri.AbsolutePath)) && string.IsNullOrWhiteSpace(filterUri.Query)) {//如果页面URL不带"/"结尾,需要添加"/"然后执行301跳转。wangyunpeng. if (!string.IsNullOrWhiteSpace(filterUri.AbsolutePath) && !filterUri.AbsolutePath.EndsWith("/")) { Uri originalUri = request.Url; UriBuilder uriBuilder = new UriBuilder(originalUri.Scheme, originalUri.Host, originalUri.Port); uriBuilder.Path = string.Concat(originalUri.AbsolutePath, "/"); uriBuilder.Query = originalUri.Query; string url301 = uriBuilder.ToString(); response.RedirectPermanent(url301, true); return; } } string physicalPath = request.PhysicalPath; //指定的路径或文件名太长,文件名必须少于 260 个字符 string absoluteFilePath = string.Concat(UrlHasher.Hash(filterUri, physicalPath, "/"), NVelocityBus.DEFAULT_DOCUMENT_SUFFIX); //所有url全部hash存放到redis和本地内存中去。区分URL大小写。wangyunpeng。2016-7-20。 request.RequestContext.HttpContext.Items[NVelocityBus.NVELOCITY_TARGET_FILE_PATH] = absoluteFilePath; //设置放入上下文对象里面获取,2015-11-17。wangyunpeng //读取本地缓存(一级缓存) string fileContent = DataCacheBus.Get(absoluteFilePath) as string; //读取Redis缓存(二级缓存) if (NVelocityBus.CACHE_REDIS && string.IsNullOrWhiteSpace(fileContent)) { try { fileContent = "";//StackExchangeRedisBus.StringGet(absoluteFilePath); } catch (Exception) { } if (!string.IsNullOrWhiteSpace(fileContent)) {//放进缓存中(一级缓存) DataCacheBus.Insert(absoluteFilePath, fileContent = ZipHelper.GZipDeCompress(fileContent), DateTime.Now.AddMinutes(NVelocityBus.CACHE_DATETIME)); } } //如果仅采用本地缓存策略,需要从磁盘上读取静态文件。(防止清除本地缓存后访问量过大,改从静态文件读取) if (!NVelocityBus.CACHE_REDIS && string.IsNullOrWhiteSpace(fileContent)) { string physicalFilePath = null; string fileExtension = IOHelper.GetFileExtension(absoluteFilePath); if (!string.IsNullOrWhiteSpace(fileExtension) && fileExtension.Equals(NVelocityBus.DEFAULT_DOCUMENT_SUFFIX, StringComparison.CurrentCultureIgnoreCase)) { physicalFilePath = NVelocityBus.GenerateFilePath(absoluteFilePath); DateTime fileDateTime = IOHelper.GetFileMaxDateTime(physicalFilePath); if (fileDateTime != DateTime.MinValue) { //如果静态文件存在 if (fileDateTime.AddDays(NVelocityBus.CACHE_DAY) > DateTime.Now) { //只读CACHE_DAY天之内的静态文件. fileContent = IOHelper.GetFileContent(physicalFilePath); } else { //超过CACHE_DAY天之外的静态文件会自动删除. IOHelper.DeleteFile(physicalFilePath); } } } if (!string.IsNullOrWhiteSpace(fileContent) && !string.IsNullOrWhiteSpace(physicalFilePath)) { //如果读取磁盘上的文件成功 DataCacheBus.Insert(absoluteFilePath, fileContent, TimeSpan.FromMinutes(NVelocityBus.CACHE_DATETIME), physicalFilePath); //放入本地缓存(一级缓存) } } if (!string.IsNullOrWhiteSpace(fileContent)) { NVelocityBus.Output(request, response, fileContent, absoluteFilePath, true); } fileContent = null; absoluteFilePath = null; }
/// <summary> /// 将文件内容输出到客户端客户端浏览器,支持http头操作。 /// </summary> /// <param name="request"></param> /// <param name="response"></param> /// <param name="fileContent">要往客户端浏览器输出的文本内容</param> /// <param name="isHttpHead">True表示使用http头操作,False表示不使用http头操作</param> internal static void Output(HttpRequest request, HttpResponse response, string fileContent, string absoluteFilePath, bool isHttpHead) { if (string.IsNullOrWhiteSpace(fileContent)) { return; } //wangyunpeng 增加http头,把生成的路径输出出来,方便修改页面使用。 if (absoluteFilePath != null) { response.AddHeader("X-Page-Hash", absoluteFilePath); } HttpCachePolicy cache = response.Cache; cache.SetOmitVaryStar(true);//http://www.cnblogs.com/dudu/archive/2011/11/03/outputcache_Bug_vary.html //#if DEBUG //本地调试不使用浏览器缓存 //cache.SetCacheability(HttpCacheability.NoCache); //cache.SetExpires(DateTime.UtcNow.AddYears(-1)); //cache.SetMaxAge(TimeSpan.Zero); //cache.SetProxyMaxAge(TimeSpan.Zero); //cache.SetNoServerCaching(); //cache.SetNoStore(); //cache.SetNoTransforms(); //#else string ifModifiedSince = request.Headers["If-Modified-Since"]; if (isHttpHead) { if ( !string.IsNullOrWhiteSpace(ifModifiedSince) && TimeSpan.FromTicks(DateTime.Now.Ticks - TypeParseHelper.StrToDateTime(ifModifiedSince).Ticks).Minutes < CACHE_DATETIME) { response.StatusCode = (int)System.Net.HttpStatusCode.NotModified; response.StatusDescription = "Not Modified"; response.End(); return; } else { cache.SetLastModifiedFromFileDependencies(); cache.SetETagFromFileDependencies(); cache.SetCacheability(HttpCacheability.Public); cache.SetExpires(DateTime.UtcNow.AddMinutes(CACHE_DATETIME)); TimeSpan timeSpan = TimeSpan.FromMinutes(CACHE_DATETIME); cache.SetMaxAge(timeSpan); cache.SetProxyMaxAge(timeSpan); cache.SetLastModified(DateTime.Now); cache.SetValidUntilExpires(true); cache.SetSlidingExpiration(true); } } //#endif System.Text.Encoding encoding = IOHelper.GetHtmEncoding(fileContent) ?? NVelocityBus._GlobalizationSection.ResponseEncoding; response.ContentEncoding = encoding; response.ContentType = response.ContentType; response.Write(fileContent); //压缩页面 if (request.ServerVariables["HTTP_X_MICROSOFTAJAX"] == null) { if (NVelocityBus.IsAcceptEncoding(request, GZIP)) { response.Filter = new GZipStream(response.Filter, CompressionMode.Compress); NVelocityBus.SetResponseEncoding(response, GZIP); } else if (NVelocityBus.IsAcceptEncoding(request, DEFLATE)) { response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress); NVelocityBus.SetResponseEncoding(response, DEFLATE); } } //强制结束输出 response.End(); }