显示数据库中的图片

目前为止,本章示例关注的重点在于获取来自数据库的文本、数字和日期信息。然而,数据库还经常存储一些其他的二进制数据,例如图片。假设 Product表使用二进制字段保存了每个产品的图片。在ASP.NET Web页面中获取该数据很容易,但是显示它却不简单。主要问题是为了在HTML页面中显示数据,需要添加图片标记,利用src属性连接独立的图片文件。例 如,

<%@ Page Language="C#" %>
<img src="Image.gif" />





不幸的是,如果动态显示图片数据,就不能使用这种方法。虽然可以在代码中设置src属性,但是无法以编程方式设置图片内容。可以首先在Web服务器 的硬盘上,将数据存储为图片文件,但是这种方法明显很慢,浪费空间,如果在同一时间受理了多个请求,则可能引起并发错误,它们可能会试图写同一个文件。

解决这个问题有两种方法。一种方法是将所有图片都存储为独立文件,数据库记录只需简单的存储文件名称,接着将文件名绑定到服务器端图片上。这是一种 很合理的解决方案,但是这种方法在某些情况下却无能为力。例如将图片存储于数据库中,以便利用RDBMS的缓存数据、记录日志和备份功能时。

规则提示,只要图片不是很大(例如,不超过50M),同时不需要利用其他应用程序频繁编辑图片,就可以将图片存储在数据库中。

在这些情况下,解决方案使用可直接返回二进制数据的ASP.NET方法。然后,在Web页面的控件中就可以使用这些二进制数据。为了完成这个任务, 需要实现数据绑定和编写自定义ADO.NET代码。对于本示例,获取AdventureWorks数据库的Product表中存储的每个产品的图片,接着 随同产品详情显示这些图片。这些图片存储在ProductPhoto表中,同时使用另一个ProductProductPhoto表链接Product和 ProductPhoto表。在本示例中使用的ASP.NET页面将在获取产品细节的同时获取产品图片,同时使用GridView控件显示这些信息。以下 是实现这个功能所需的步骤:

① 创建执行获取图片的HttpHandler。
② 在Web.config文件中注册HttpHandler。
③ 使用HttpHandler在GridView中显示图片。

以下章节将详细讲解每个步骤。

1.创建HttpHandler

ASP.NET提供了一个低级别的请求/响应API,以HTTP处理程序格式组织,用于自定义方式处理和维护输入HTTP请求。为了实现这一功能,可编写一个实现System.Web.IHttpHandler接口的类,同时实现IHttpHandler接口的方法。

对于处理HTTP请求,当使用高级页面框架抽象来提供服务不是很有效时,使用处理程序却往往能发挥作用。处理程序通常用于过滤器和类似的CGI应用 程序,尤其是可用于返回二进制数据。ASP.NET收到的每个输入HTTP请求最终都是由一个特定的实现IHttpHandler接口的类实例来处理。
如 前文所述,所有HttpHandler必须实现IHttpHandler接口。某些内置的类(例如HttpApplication)和Page类都已经实 现了IHttpHandler接口。IHttpHandler接口很简单。该接口包括只读属性IsReusable,该属性返回Boolean值(通常为 true),用于指示是否另一个请求能够使用IHttphandler实例。同时,该接口还包括ProcessRequest()方法,该方法包含 HttpContext类型的参数,用于执行处理扩展的工作。

bool IsReusable {get;}
void ProcessRequest(HttpContext context);

通过提供给ProcessRequest()方法的HttpContext对象,能够获取针对Request和Response对象的引用。

既然读者已经了解了HttpHandler的基本知识,那么就可以创建一个名为ImageHandler的HttpHandler类(表示为 ImageHandler.ashx),该处理程序能够获取ProductPhoto表中的图片。示例10-4列举了ImageHandler的实现代 码。

示例10-4:获取图片的HttpHandler

using System;
using System.Data;
using System.Data.SqlClient;
using System.Web;
using System.Web.Configuration;
public class ImageHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string connectionString = WebConfigurationManager.ConnectionStrings
["AdventureWorks"].ConnectionString;
// 获取请求ID
string photoID = context.Request.QueryString["PhotoID"];
if (photoID == null)
throw new ApplicationException("Must specify ID");
// 创建获取相应记录的参数化命令
SqlConnection connection = new SqlConnection(connectionString);
string sql = "SELECT ThumbNailPhoto FROM Production.ProductPhoto " +
"WHERE ProductPhotoID=@ProductPhotoID";
SqlCommand command = new SqlCommand(sql, connection);
command.Parameters.AddWithValue("@ProductPhotoID", photoID);
try
{
connection.Open();
SqlDataReader reader =
command.ExecuteReader(CommandBehavior.SequentialAccess);
if (reader.Read())
{
// 指定缓冲区大小,字节流读入的缓冲区,开始写操作的缓冲区索引
int bufferSize = 100;
byte[] bytes = new byte[bufferSize];
long bytesRead;
long readFrom = 0;
// 每次读取字段的100个字节
do
{
bytesRead = reader.GetBytes(0, readFrom, bytes, 0, bufferSize);
context.Response.BinaryWrite(bytes);
readFrom += bufferSize;
} while (bytesRead == bufferSize) ;
}
reader.Close();
}
finally
{
connection.Close();
}
}
public bool IsReusable
{
get { return true; }
}
}

ProcessRequest()方法是类代码的主体。该方法首先从查询字符串中获取PhotoID。然后使用该值在ProductPhoto表中执行SQL查询,该查询根据图片ID返回ThumbNailPhoto列值。

当开始处理查询结果时,会逐段读取ProductPhoto表的ThumbNailPhoto列,接着使用 Response.BinaryWrite()方法将数据以块方式写入输出流中。为了实现这一点,可指示SqlDataReader对象使用它所支持的顺 序访问功能。为了使用顺序访问功能,需要向SqlCommand.ExecuteReader()方法提供Command对象的 CommandBehavior. SequentialAccess值。然后,使用SqlDataReader.GetBytes()方法每次整体移动一行。

当使用顺序访问时,要记住一些限制。首先,必须将数据作为只向前的流进行读取。一旦读取了一块数据,会自动移向流中的前面数据,并且不能后退。其 次,必须使用与查询返回的字段的相同顺序来读取字段。例如,如果查询返回3个列,第三个列是二进制字段,那么必须在访问第三个字段的二进制数据之前,返回 第一个和第二个字段的值。如果首先访问第三个字段,则不能访问前两个字段。

GetBytes()方法返回获取的字节数的值。如果需要确定字段的总字节数,只需在调用GetBytes()方法时传递空引用,而不是缓冲区。


2.注册处理程序


既然已经创建了处理程序,下一步是在Web.config文件中注册处理程序。在这之前,需要将编译后的程序集(包括 ImageHandler类)置于Web站点根目录下的bin文件夹中。在将程序集存储到bin文件夹中之后,可根据如下方式在Web.config文件 的<httpHandlers>节中注册处理程序:

<system.web>
<httpHandlers>
<add  verb="GET" path="ImageHandler.ashx" type="ImageHandler" />
</httpHandlers>
----
</system.web>

以上用于在Web.config文件中注册程序集。使用以上注册,所有针对ImageHandler.ashx的HTTP GET请求都将由ImageHandler类实例处理。

3.使用HttpHandler

既然已经创建和注册了HttpHandler,那么应该在ASP.NET页面中使用它,以便显示图片。示例10-5列举了ASP.NET页面代码。

示例10-5:使用ASP.NET页面的ImageHandler

<%@ Page Language="C#" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Displaying Images using GridView</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView ID="categoryView" runat="server"
AutoGenerateColumns="false" DataSourceID="categorySource"
HeaderStyle-BackColor="blue" HeaderStyle-ForeColor="White">
<Columns>
<asp:TemplateField HeaderText="Category">
<ItemStyle VerticalAlign="Top" Width="20%" />
<ItemTemplate>
<table border='1'><tr><td>
<img src='ImageHandler.ashx?PhotoID=<%# Eval("ProductPhotoID")%>' />
</td></tr>
</table>
<b><%# Eval("ProductID") %></b><br />
<%# Eval("Name") %><br /><br />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:SqlDataSource ID="categorySource" Runat="server"
SelectCommandType="Text"
SelectCommand="Select A.ProductID As ProductID,
A.Name As Name, B.ProductPhotoID As ProductPhotoID From Production.Product
A Inner Join Production.ProductProductPhoto B On A.ProductID = B.ProductID"
ConnectionString="<%$ConnectionStrings:AdventureWorks%>">
</asp:SqlDataSource>
</div>
</form>
</body>
</html>
以上代码行调用图片处理程序来返回产品ID对应的图片,该产品ID内嵌于GridView控件的ItemTemplate:
<img src='ImageHandler.ashx?PhotoID=<%# Eval("ProductPhotoID")%>'/>
正如所看到的代码,ProductPhotoID以查询字符串的形式传递给图片处理程序。图10-5显示了页面生成的输出。
  
图10-5
如 果生成包括单个记录信息的页面,那么使用当前HttpHandler方法会取得很好效果。例如,可以显示一个产品列表,然后当用户选中某产品时,显示对应 产品的图片。然而,如果一次为每个产品都显示图片数据(例如在列表控件中),这种方法效率不高。虽然这种方法仍然可用,但是效率不高,因为该方法对获取的 每个图片都使用了独立的HttpHandler请求(从而包括一个独立的数据库连接)。通过在从数据库获取图片数据之前,检查缓存中的图片数据的方法,能 够解决这个问题。在绑定GridView之前,可执行一个查询,该查询返回包括图片数据在内的所有记录,接着将图片加载到缓存中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值