【翻译】图像缓存,在ASP.NET中缓存图像

原文地址:http://www.codeproject.com/KB/aspnet/CachingImagesInASPNET.aspx



一个最简单但是最有效的提高web应用程序性能的方法是:在客户端缓存图像。


简介

有许多方法可以提高web应用程序的性能,一个最简单但是最有效的提高web应用程序性能的方法是,在客户端缓存图像。在这篇文章中


我将展示怎样给我们的DotNetNuke站点实现图片缓存。



问题

当我在搭建网站http://www.software-architects.com的时候,我在制作菜单项的时候,在CSS中使用了很多的图像作为背景。在把这些文件上传到网站服务器之后,我用Microsoft Network Monitor测试下了一个请求会产生多少的流量。这个工具可以捕获和分析网络的流量。有可以从微软下载中心

(Microsoft Download Center).

下载到它。



用Microsoft Network Monitor 3.1 我记录了对http://www.software-architects.com的一次请求。结果在一个页面中,我得到了对于20个不同的文件夹的20个不同的请求。Microsoft Network Monitor 显示大约有一半的请求是针对对菜单中的图片的。



MNM_withoutCaching.png

有两种不同的方法避免这个问题。一种是,你可以告诉IIS在客户端缓存图片;另外一个方法是,直接在ASP.NET中做这个(这个方法更复杂些)


在IIS中缓存图像

在IIS中缓存是非常简单的。打开IIS,在左侧或者是右侧面板中选择一个文件夹,打开属性对话框。

IIS_Images.png

勾上“启用内容过期(Enable content expiration)”并且选择你的内容什么时候过期。



IIS_CachingProperties.png

这样就行了!IIS用“cache-control”告诉客户端内容可能被缓存在客户端。 "expires" 里边包含了过期的日期。所以客户端知道在这个日期之后,它必须请求服务器来或得新的内容。



如果符合下边这两点,这个方法将很好用:


  • 你可以把你所有的图片和其他要缓存的文件放到一个或者少数几个文件夹中。
  • 最重要的是,你有权限访问IIS。

在我们的案列中,这两个条件都不能满足。在我们的

DotNetNuke 工程中,图片被分布在多个文件夹中。所以配置IIS是非常复杂的。并且最重要的是,我们的空间商没有给我们访问IIS的权限。因此,我们必须寻找另外的解决方案。
用自定义个HttpHandler缓存图片

第一件事情,我们要解决的是绕过IIS得到ASP.NET的请求。我决定写一个自定义的HTTP handler,它可以监听"*.gif .ashx,".jpg .ashx "和“.png.ashx”这些类型的文件。你可以在这个网站找到关于IHttpHandler一篇好文章

Use local scope to improve performance.
我在vs中用
CachingHandler这个类建了一个类的工程,它可以处理对图片的请求。 CachingHandler 实现了了IHttpHandler的接口

,就像page类一样。这个接口提供了

IsReusable 属性和ProcessRequest方法。
IsResuable 表明

另外一个请求是否可以复用这个Http handler。


ProcessRequest 是关于线程安全的 。
ProcessRequest

做真正的工作。它得到当前的内容,并对发送给客户端的结果负责


 namespace SoftwareArchitects.Web



{



public class CachingHandler : IHttpHandler



{



public bool IsReusable



{



get { return true; }



}



public void ProcessRequest(HttpContext context)



{...



}



}



}



我们想让我们的HTTP handler发送个文件给客户端。当我们用这些路径 *.gif.ashx, *.jpg.ashx



*.png.ashx监听文件的时候,我们所必须做到就是把”.ashx“从请求的路径中移除来得到我们想发送给客户端 的文件。包括我们提取的文件名和这个文件的扩展名。

public void ProcessRequest(HttpContext context)



{



string file = context.Server.MapPath



(context.Request.FilePath.Replace(".ashx", ""));



string filename = file.Substring(file.LastIndexOf('\\') + 1);



string extension = file.Substring(file.LastIndexOf('.') + 1);







下一步,我们载入从webconfig中为CachingHandler做的配置。所以我创建了个类CachingSection



(我展示的有点晚了),它包含了一个属性
CachingTimeSpan 和一个集合FileExtensions,它可以知道



每个文件扩展的内容类型。在这个类的帮助下,我们可以配置
HttpCachePolicy响应对象。
  • SetExpires 告诉客户端内容的有效期是多长。
  • SetCacheability 告诉客户端谁可以缓存内容。我们设置这个属性是 public。它意味着这个响应可以被客户端缓存起来并且可以分享缓存。
  • SetValidUnitExpires 指定ASP.NET缓存是否应该忽略客户端发送的使cookie无效的HTTP cache-control 头
  • ContentType 设置响应的MIME类型
CachingSection config = (CachingSection)context.GetSection



("SoftwareArchitects/Caching");



if (config != null)



{



context.Response.Cache.SetExpires



(DateTime.Now.Add(config.CachingTimeSpan));



context.Response.Cache.SetCacheability(HttpCacheability.Public);



context.Response.Cache.SetValidUntilExpires(false);



FileExtension fileExtension = config.FileExtensions[extension];



if (fileExtension != null){



context.Response.ContentType = fileExtension.ContentType;



}



}



最后,我们对响应添加content-disposition头告诉客户端它应该在浏览器中打开文件(inline)。另外



我们可以把文件名改成没有扩展名.ashx的名字。因为这个名字,当你尽力下载这个文件的时候才显示。然后



我们使用writeFile把这个文件发送给客户端。


  context.Response.AddHeader("content-disposition", 



"inline; filename=" + filename);



context.Response.WriteFile(file);}



在webconfig中定义自定义配



在http handler中我们使用一个自定义的类去读webconfig中的一些配置。因此,我创建了CachingSection



类,继承自
ConfigurationSection。在这个类中,我实现了一个属性CachingTimeSpan,他存储着一个



timespan的值,这个值是在客户端缓存对象的时间,并且一个属性
FileExtensions,它存储着FileExtension



对象的集合。为了标注webcofing中这些元素的属性你只不过是在每个属性中添加一个
ConfigurationProperty



属性,它可以在webconfig中设置。


namespace SoftwareArchitects.Web.Configuration



{



/// <summary>



/// Configuration for caching



/// </summary>



public class CachingSection : ConfigurationSection



{



[ConfigurationProperty("CachingTimeSpan", IsRequired = true)]



public TimeSpan CachingTimeSpan 



{



get { return (TimeSpan)base["CachingTimeSpan"]; }



set { base["CachingTimeSpan"] = value; }



}



[ConfigurationProperty("FileExtensions", IsDefaultCollection = true, IsRequired = true)]



public FileExtensionCollection FileExtensions 



{



get { return ((FileExtensionCollection)base["FileExtensions"]); }



}



}



为了支持单个的值和集合,我们必须实现一个从ConfigurationElementCollection继承的类。 在我们的例子中,我们需要一个集合来配置可以的扩展列表和他们相应的内容类型。 你可以从这个文件CachingSection.cs中下载完整的代码。

  /// <summary>



/// List of available file extensions



/// </summary>



public class FileExtensionCollection : ConfigurationElementCollection



{



...







}



最后我们需要一个针对所有扩展的类,一个属性存储扩展,一个属性存储内容类型

/// <summary>



/// Configuration for a file extension



/// </summary>



public class FileExtension : ConfigurationElement



{



[ConfigurationProperty("Extension", IsRequired = true)]



public string Extension



{



get { return (string)base["Extension"]; }



set { base["Extension"] = value.Replace(".", ""); }



}



[ConfigurationProperty("ContentType", IsRequired = true)]



public string ContentType



{



get { return (string)base["ContentType"]; }



set { base["ContentType"] = value; }



}



}



}



现在,我们所有要做的就是在webgconfig中添加配置部分。在configSecontions标签中, 我们用SoftwareArchitects这个名字添加了一个新的sectionGroup。在这个group中



我们添加了一个名叫caching的section。这个属性的类型能够说明了类和
CachingSection



的集合。当然,我们必须在bin文件加中,添加
CachingSection 类的集合。


然后我们可以用这个



配置的标签添加一个新的标签。在这个分组里边,我们用这个section的名字添加了一个新的标签,



并且在这个section中我们在cahingSection类中定义的所有属性现在都可以用了。


<configuration>



<configSections>



<sectionGroup name="SoftwareArchitects">



<section name="Caching" requirePermission="false" type="SoftwareArchitects.Web.Configuration.CachingSection, 



SoftwareArchitects.Web.CachingHandler" />



</sectionGroup>



</configSections>



<SoftwareArchitects>



<Caching CachingTimeSpan="1">



<FileExtensions>



<add Extension="gif" ContentType="image"gif" />



<add Extension="jpg" ContentType="image"jpeg" />



<add Extension="png" ContentType="image"png" />



</FileExtensions>



</Caching>



</SoftwareArchitects>



...


现在在我们使用 CahingHandler之前,有最后一件需要做的事情。我们必须把它添加到webconfig 中的httpHandlers部分。在那里,我们必须为每个我们想要标记的文件的扩展添加条目到httphandler中。 我决定支持.gif,.jpg,.png的图像。所以我增加了一个为路径*.gif.ashx,*.jpg.ashx 和*.png.ashx 的处理机制。在这个类型中,我指定了Http handler的类和集合。当然这个集合也必须 放在bin文件夹中。

 <httpHandlers>



<add verb="*" path="*.gif.ashx" type="SoftwareArchitects.Web.CachingHandler, SoftwareArchitects.Web.CachingHandler"/>



<add verb="*" path="*.jpg.ashx" type="SoftwareArchitects.Web.CachingHandler, SoftwareArchitects.Web.CachingHandler"/>



<add verb="*" path="*.png.ashx" type="SoftwareArchitects.Web.CachingHandler, SoftwareArchitects.Web.CachingHandler"/>



</httpHandlers>



</configuration>



有也可以使用其他的文件扩展名如*.gifx。但是要这样做,你必须有权限访问IIS去配置新的扩展通过 aspnet_isapi.dll来出来。因为空间商没有分配给我配置IIS的权限,所以我必须使用*.ashx,



因为它已经标注在aspnet_isapi.dll中了。



最后我在我的网站中给所有图片添加了扩展名“.ashx”(在css文件和ASPX文件中)。当我再次监视




http://www.software-architects.com 的主页,第一次请求仍然产生二十个对服务器的请求,



但是第二次的时候,它只产生了七次请求来载入页面,因为这些图像已经缓存到客户端了。







MNM_withCaching.png 你可以从我的网站的这个网址 http://www.software-architects.at/TechnicalArticles/CachinginASPNET/tabid/75/language/de-AT/Default.aspx.



来查看他们怎么工作的。在一个图片上右击并且打开属性对话框。你将看到,那个URL是以.ashx结尾的。



当你右击并且选择“将图片另存为(Save Picture as)”,刚才那个文件名不包括.ashx的扩展,这是因为
content-dispositiont头



当然,你也可以用这个处理机制来处理其他的文件例如 css和javascript文件。因此你能又一次减少请求的数量。




测试 CachingHandler



你可以很轻松的用简单的站点来测试缓存图像。我在vs解决方案中用CachingWebSite的名字添加了一个网站工程,你可以试试他怎么工作的 (下载完整的解决方案download complete solution)一方面,这个站点包含一defalut.aspx页面,页面里有个image标签。你可以看到 那个图片是以.ashx结尾的。

<img src="/Portals/1/App_Themes/Standard/Images/LogoWithMenuBackground.png.ashx" />



另外一方面,这个网站包含了一个主题Stanard和一个样式表文件。在这个样式表中我使用了一个背景图像。同样这个图片也是以.ashx结尾。



body {margin: 0px;padding: 0px;background-image: url(Images/MenuBackground.png.ashx);background-repeat: repeat-x;}



在web.congfig中,我插入了自定义的部分配置CachingHandler 并且一个扩展一个http handler, 和上边解释的一样。此外我在System.web部分添加了一个跟踪(trace)标签来跟踪对每个文件的每个请求

<trace enabled="true" pageOutput="false" requestLimit="50" mostRecent="true" />当我运行网站,我看到带着logo的defaul.aspx页面,还有定义在css中的背景图像。


 




 


CachingWebSite.png 为了看到跟踪信息,我在ie中打开一个新的标签并且在url中用Trace.axd替换default.aspx这个信息显示了打开default.aspx需要有四个必要的请求。第一次和用户每次按F5的时候,所以文件被发送到客户端。 Trace_withoutCaching.png 当我转回到第一个标签,有三种可能重新载入页面,我可以


  • 按F5

  • 单击 "Reload page ..." 链接
  • 单击 "Reload page ..." 按钮


按f5可以重新载入所有的内容,但是当点击链接或者按钮的时候仅仅是重新载入那些没有被缓存 到客户端的内容。在下边的截图中,我点击了链接和按钮。正如你看到的,只有对default.aspx Trace_withCaching.png 如果用户通过超链接或者是后退进入这个页面,那么只有没有被缓存到客户端的文件才会从服务器上请求。 第5个和第6个请求是通过点击链接引起的,而第7个和第8个是单击按钮引起的。

Tags:  图像缓存 数据缓存 asp.net 缓存

延伸阅读

最新评论

发表评论