从 .NET 程序集提供图像(二)

程序

目录:
增强代码
整理
安全性
小结
关于作者

增强代码

代码中首先要处理的是大小写形式。HTTP 认为以下所有 URL 都相同,因为 URL 不区分大小写。

<img src=http://www.163design.net/n/a/".mfr?assem=ImageServerℑ=winxp.gif" />
<img src=http://www.163design.net/n/a/".mfr?assem=ImageServerℑ=winxp.gif" />
<img src=http://www.163design.net/n/a/".mfr?assem=ImageServerℑ=winxp.gif" />

我们的代码目前有一个问题,由于它不保留原 HTTP 请求不区分大小写的特征,因此必须向 LoadAndReturnImage 函数再添加一些代码。

不区分大小写

我们要做的是从程序集加载图像而不考虑大小写,但由于 Assembly.GetManifestResourceStream 是区分大小写的,因此还得另想办法。Assembly.GetManifestResourceNames() 函数将返回给定程序集内的所有资源列表,因此我们要做的就是调用此列表,与这些资源名称进行不区分大小写的比较,然后使用查找出的名称:

Assembly resourceAssem = Assembly.Load ( assembly ) ;

// 查找缓存的名称
string[] names = HttpContext.Current.Application [ assembly ] as string[] ;

if ( null == names )
{
// 获取程序集内所有资源的名称
names = resourceAssem.GetManifestResourceNames() ;
Array.Sort ( names , CaseInsensitiveComparer.Default ) ;
HttpContext.Current.Application [ assembly ] = names ;
}

// 如果此程序集内存在一些资源,
// 检查所需的资源
if ( names.Length > 0 )
{
// 在名称数组中查找图像
int pos = Array.BinarySearch ( names , image ,
CaseInsensitiveComparer.Default ) ;

if ( pos > -1 )
WriteImage ( resourceAssem , names[pos] , true ) ;
}

这里我加载了包含资源的程序集,然后查找应用程序缓存以查看该程序集内的资源列表是否已被加载和缓存。如果没有,我通过调用 Assembly.GetManifestResourceNames() 来读取资源列表,然后对列表进行排序,并将其存储在应用程序状态中。

然后,我就可以使用 Array.BinarySearch() 方法对名称列表执行二进制搜索。这比按顺序搜索字符串列表要快得多,且在应用程序状态存储资源列表所需的系统开销也较小。

这样就解决了区分大小写的问题,但性能如何呢?目前,每次当图像请求到达时,我们都要调用全部代码 - 除了最小的 Web 站点之外,其余所有的请求都可能会造成严重的性能问题。下一节我们将处理这个问题。

缓存

像普通的图像和一些 ASPX 页面一样,把从程序集返回的图像进行缓存是很有用的 - 因为这些图像驻留在程序集中,一般不会频繁地更改。

如果我们在编写一个简单的 ASPX 页面,则可以添加 OutputCache 指令以缓存页面。但在我们的方案中,我们需要一种方法能够通过编程方式将缓存控件标题添加到响应流中。幸运的是,在 ASP.NET 中这很容易完成。在把图像写入输出流的函数中,只需添加以下几行:

response.Cache.SetExpires ( DateTime.Now.AddMinutes ( 60 ) ) ;
response.Cache.SetCacheability ( HttpCacheability.Public ) ;
response.Cache.VaryByParams["assem"] = true ;
response.Cache.VaryByParams["image"] = true ;
// 将图像写入响应流...

此设置使图像在一小时(这个时间显然可以延长以减少服务器负载)后过期,并定义图像可以在任意位置(客户端、代理服务器、服务器等)进行缓存。它还定义了更改缓存行为的参数。现在,代码几乎已经完成了,但我们需要决定如何处理异常情况。

关于异常的编程

我们的代码中可能会引发很多异常。现在,用户的浏览器可能会断开链接,甚至可能仍然会遇到 ASP.NET 错误页面。我们可以推测出很多种可能发生的情况。如下所示:

·程序集可能不存在。
·程序集存在但不包含任何图像。
·程序集可能不包含所请求的图像。

代码也可能会造成其他错误。当找不到图像时,浏览器默认的响应是返回一个带有红十字的图像以表示一个断开的链接。

您当然希望用自己的默认图像来代替此图像。我已将一个默认的断开链接图像包含在 ImageServer 程序集中,当发生异常时,该图像将返回到浏览器。此行为可以通过在 web.config 文件的 AppConfig 部分添加一个设置来实现。

当发生错误时,如果要覆盖默认行为(返回链上的异常),请将以下内容添加到 web.config 中。

<appSettings>
<add key="MFRShowBrokenLink" value="true" />
</appSettings>

现在,当代码中出现异常时,将向浏览器返回断开链接图像,并在跟踪日志中写入警告。

图4:链接断开时返回的图像

如果查看跟踪日志,您会看到有关图像不存在的项,该项与下面类似。

图5:无效图像请求的示例跟踪日志输出

本文讨论的所有代码都可以通过本页顶部的 MFRImages.exe 下载链接获得。此下载包括本节完成的所有增强工作。还包括一些测试页,通过这些测试页可以查看使用处理程序和 ASPX 方法来呈现图像的结果。

整理

下面要添加一种方法,以返回驻留在程序集内的图像的正确 URL,然后自定义控件编写人员(或是您)可以调用此方法来返回图像。

如果已选择了处理程序方法来提供图像,则您所需的函数如下。

public static string ConstructImageURL ( Assembly assembly, string image )
{
return string.Concat ( ".mfr?assem=" ,
HttpUtility.UrlEncode ( assembly.FullName.ToString ( ) ) ,
"ℑ=" ,
HttpUtility.UrlEncode ( image ) ) ;
}

对于这段代码,我使用的是 string.Concat(),因为它比 string.Format() 大约快 4 倍。每个小技巧都会有所帮助! 然后可以用它来设置您在自定义控件中创建的所有图像的 ImageURL 属性。

安全性

到目前为止的讨论中,我们一直基于程序集名称和资源名称提供图像。这没什么不好,但这意味着任何人都可以得到磁盘上的程序集名称,并可以尝试通过将其他程序集名称传递给处理程序来进行攻击。

为了避免这个潜在的问题,最好用某种方法对返回的值进行加密。我们可以提供一些从程序集名称和图像名称生成的散列码,或使用程序集名称和图像名称的加密格式,然后在接收到请求后再进行解密。

前一种方法(使用散列码)需要服务器中有查找表,并且表中为每个提供的图像填充了内容。这就给 Web 领域带来一个潜在的问题。在 Web 领域,可能一个服务器提供初始图像请求(并缓存散列码),而另一个服务器实际响应图像。

因此,我选择了第二种方法,即在返回到用户的 URL 中包含加密的程序集名称和图像名称。这样就不会遇到 Web 领域中存在的问题,但却意味着需要从浏览器多传送一些数据到服务器,因为图像 URL 要长一些。

示例代码包含一个类,它使用 Triple-DES(数据加密标准)算法加密和解密字符串。通常,程序集名称和图像名称在传递到客户端之前已进行了加密。当请求图像时,这些值被解密,并调用与原来相同的代码。

我已将这些内容以可配置的方式添加到解决方案中。在 web.config 中仅有一个标志,如果设置为“true”,则会在向客户端提供资源名称时对其进行加密:

<appSettings>
<add key="MFRSecure" value="true" />
</appSettings>

在处理程序的 ProcessRequest 方法中,我对此标记进行检查:

bool secure = false ;
string shouldSecure = ConfigurationSettings.AppSettings["MFRSecure"] ;

if ( null != shouldSecure )
secure = Convert.ToBoolean ( shouldSecure ) ;

string assembly = context.Request.QueryString["assem"] ;
string image = context.Request.QueryString["image"] ;

if ( secure )
{
assembly = Crypto.Decrypt ( assembly ) ;
image = Crypto.Decrypt ( image ) ;
}

ManifestImageLoader.RenderImage ( assembly , image ) ;

类似地,在前面介绍的 ConstructImageURL 方法中,在程序集名称和图像名称被传递给客户端之前,我对它们进行了加密。代码的很多部分都可以进行扩展或改进。下面是我的几点建议。

·当无法找到资源时,配置项不对使用的图像进行硬编码,而是指定图像的 URL。这样在出现异常时,您就可以从磁盘(或从其他程序集)加载特定的图像并将其返回到浏览器。·图像的缓存超时也可以定义为配置项。
·可以扩展代码,以允许从程序集提供任何类型的图像 - 目前,mime 类型被硬编码为 image/GIF。
·对于为何此示例中的代码不能提供程序集内的其他资源,没有什么原因 - 您完全可以提供 TXT 文件、WAV 文件等。

小结

本文介绍了两种方法,用于从程序集检索格式适合包含在 Web 站点中的图像。第一种方法是从 ASPX 页面提供图像,这种方法简单而且不需要修改 Web 服务器配置,但是提供图像的 ASPX 页面的路径必须正确,以使图像能够正确显示。

另一种方法是从自定义处理程序提供图像。这种方法克服了基于路径的限制,但需要更改 IIS 配置数据库,以允许由 aspnet_isapi.dll 扩展程序提供 .mfr 扩展名。而且还要为给定的应用程序修改 web.config。我个人建议使用处理程序方法而不要使用 ASPX 方法,因为在 Web 服务器中配置处理程序方法后,使用起来会更容易(而且不需要路径)。

关于作者

Morgan 是 Microsoft 在英国工作的应用程序开发顾问,专攻 Visual C#、控件、WinForms 和 ASP.NET。自从 2000 年发布 PDC 以来,他就从事 .NET 工作,并且非常喜欢 .NET,因此加盟该本公司。他的主页是 http://www.morganskinner.com/,在那儿您可以找到他写的其他一些文章的链接。在有限的闲暇时间里,他喜欢在自家的花园中锄锄草,或者享受几块风味独特的菜肉烘饼。

时间: 2024-10-25 02:46:59

从 .NET 程序集提供图像(二)的相关文章

从 .NET 程序集提供图像(一)

程序 摘要:大多数 Web 站点的用户界面都需要有图像,这些图像通常存储在磁盘上.本文介绍如何从程序集提供图像.从程序集提供图像可以避免众多文件散布在磁盘上,简化 Web 服务器的安装和配置,以及提高图像的安全性.(本文包含一些指向英文站点的链接) 适用于:Microsoft .NET Framework 版本 1.0 和 1.1Microsoft Visual C#Microsoft ASP.NET 下载 MFRImages.exe 示例文件.下载内容中包括 readme.htm,用于说明如何

验证码识别原理浅谈--图像二值化

二值化实现方法有 1.1图像灰度化-中值滤波等,同时可以实现背景的去除 1.2图像灰度化-根据灰度值 1.3根据图像色系范围进行二值化处理 图像二值化的意思就是将图像的有效点给分离出来,然后将图片用0和1两个值进行表 示,因为我们没必要知道他们的具体色值,我们的目的就是要知道他们的具体形状,他们 的特征 譬如说淘宝的验证码,和盛大的验 证码, 他们的有效点无非就是U3FM和YPZ7等字符所占据的空间,我们不用管他们是黑色还是红色 ,我们将所有他们所占据的位置都给置为1,其他位置都给置为0 然后我

图像处理-图像二维匹配和立体匹配的区别?

问题描述 图像二维匹配和立体匹配的区别? 图像的二维匹配是两幅照片之间的特征点的匹配,但是立体匹配 也是两幅照片的匹配然后生成视差值,得到深度图,为什么前者就是二维匹配,而后者却叫立体匹配(立体也就意味是三维匹配),这是为什么呢,三维匹配不应该是点云匹配才叫三维吗

内存分配-利用MFC处理图像二值化一直出错,而且是运行完成后出错

问题描述 利用MFC处理图像二值化一直出错,而且是运行完成后出错 代码如下希望大大们解答!!!好纠结 BOOL CCarDoc::getotsu(unsigned char * lpDIBBits, LONG lWidth, LONG lHeight) { unsigned char * lpNewDIBBits; unsigned char* lpDst; LONG lLineBytes; // 计算图像每行的字节数 lLineBytes = (((lWidth * 8) + 31) / 32

图像处理-(C++编程)已经将图像二值化将ROI区拿出来了,怎么样将其对应到原图当中去呢

问题描述 (C++编程)已经将图像二值化将ROI区拿出来了,怎么样将其对应到原图当中去呢 已经将图像二值化将ROI区与背景分离出来了,怎么样将其对应到原图当中去呢,因为接下来我只想对原图当中的ROI区的原始数据做处理,求思路,有代码更好,拜托拜托 解决方案 http://www.linuxidc.com/Linux/2013-01/78173.htm 解决方案二: http://blog.csdn.net/icvpr/article/details/8515596 解决方案三: 读进表格,从表格

ASP.NET MVC以ValueProvider为核心的值提供系统 二

DictionaryValueProvider NameValueCollectionValueProvider采用一个NameValueCollection作为数据源,DictionnaryValueProvider的数据源类型自然就是一个Dictionnary.NameValueCollection和Dictionnary都是一个键值对的集合,它们之间的不同之处在NameValueCollection运行元素具有相同的Key,Dictionnary却要求元素的Key具有唯一性. 一.Dict

用SQL Server为Web浏览器提供图像1

微软SQL Server数据库服务器能够在SQL数据库里保存图片和大量的文本.图片和文本使用的数据类型分别是image图片类型和text文本类型.假如使用VB或VC开发前端应用程序,在windows操作系统上运行,那么可以容易地从SQL数据库里提取图片数据,把它放入一个文件,并在屏幕上显示出来.但是,要是想从SQL Server得到图片,并在Web浏览器里显示,就不那么容易了.虽然在微软SQL Server 6.5里提供了Web助理和存储过程 sp_makewebtask.sp_runwebta

用SQL Server为Web浏览器提供图像2

建立ActiveX 动态链接库     使用 VB 5.0,我们要开发一个ActiveX 动态链接库.这个动态链接库让我们能够连接到SQL数据库,提取图片数据,并把它们保存成文件,放在目录里.要建立动态链接库,请在打开VB程序之后执行下列步骤:首先:在文件(File)菜单里,选择新建项目(New Project).接着,在新建立项目对话框里,双击ActiveX DLL 图标.VB自动地向项目里增加一个类,叫做Class1.然后,在属性容器里,双击名称(Name)属性,把它改成clsImageLo

用SQL Server为Web浏览器提供图像

微软SQL Server数据库服务器能够在SQL数据库里保存图片和大量的文本.图片和文本使用的数据类型分别是image图片类型和text文本类型.假如使用VB或VC开发前端应用程序,在windows操作系统上运行,那么可以容易地从SQL数据库里提取图片数据,把它放入一个文件,并在屏幕上显示出来.但是,要是想从SQL Server得到图片,并在Web浏览器里显示,就不那么容易了.虽然在微软SQL Server 6.5里提供了Web助理和存储过程 sp_makewebtask.sp_runwebta