【翻译】在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 显示大约有一半的请求是针对对菜单中的图片的。

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

在IIS中缓存图像

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

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

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

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

  • 你可以把你所有的图片和其他要缓存的文件放到一个或者少数几个文件夹中。
  • 最重要的是,你有权限访问IIS。
在我们的案列中,这两个条件都不能满足。在我们的用自定义个HttpHandler缓存图片

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

我在vs中用

 namespace SoftwareArchitects.Web
{
public class CachingHandler : IHttpHandler
{
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{...
}
}
}
我们想让我们的HTTP handler发送个文件给客户端。当我们用这些路径*.png.ashx监听文件的时候,我们所必须做到就是把”.ashx“从请求的路径中移除来得到我们想发送给客户端
的文件。包括我们提取的文件名和这个文件的扩展名。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

(我展示的有点晚了),它包含了一个属性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把这个文件发送给客户端。 + filename);
context.Response.WriteFile(file);}
在webconfig中定义自定义配

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

类,继承自CachingTimeSpan,他存储着一个

timespan的值,这个值是在客户端缓存对象的时间,并且一个属性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继承的类。

在我们的例子中,我们需要一个集合来配置可以的扩展列表和他们相应的内容类型。

你可以从这个文件class FileExtensionCollection : ConfigurationElementCollection

{

...

}

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

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标签中,

我们用sectionGroup。在这个group中

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

的集合。当然,我们必须在bin文件加中,添加然后我们可以用这个

配置的标签添加一个新的标签。在这个分组里边,我们用这个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 的主页,第一次请求仍然产生二十个对服务器的请求,

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

你可以从我的网站的这个网址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结尾的。

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

body {margin: 0px;padding: 0px;background-image: url(Images/MenuBackground.png.ashx);background-repeat: repeat-x;}CachingHandler 并且一个扩展一个http handler,
和上边解释的一样。此外我在System.web部分添加了一个跟踪(trace)标签来跟踪对每个文件的每个请求