在ASP.NET 2.0中操作数据之五十三:在Data Web控件显示二进制数据_自学过程

导言:

  在前面的教程我们阐述了应用程序处理二进制数据的2种模式,以及使用FileUpload 控件从浏览器向服务器文件系统上传文件。当文件上传并存储在文件系统里时,应在相应的数据库记录里存储该文件的存储路径。

  我们先来看如何为最终用户提供二进制数据。怎样展示二进制数据呢?这取决于其类型。比如图片,我们将其显示为image;如果是PDFs,Microsoft Word文档、ZIP文件或其它类型的数据,或许提供一个“Download”链接比较妥当。

  在本节,我们看如何在GridView和DetailsView一类的数据Web控件里呈现二进制数据,在后面的教程我们将注意力转向将上传文件和数据库联系起来。

第一步:提供BrochurePath值

  表Categories的Picture列存储相关类的图片信息。具体的讲,为16色的低质量位图,大小为172乘120像素,约11 KB。另外还包括一个约78字节的OLE报头,在显示图片的时候需要将其剥离。为什么会有报头信息呢?因为数据库Northwind源于微软的Access数据库。在Access里二进制数据OLE类型来存储的,该类型会添加报头。现在,我们看如何从图片剥离报头,以便显示。在后面的教程我们将创建一个界面,将带报头的这些位图替换为不带报头的等价的JPG图片。

  前面我们考察了如何使用FileUpload控件,让我们继续为服务器文件系统添加文件。不过暂时不用更新Categories表的BrochurePath列,那是下一章的内容。我们现在需要手工为BrochurePath赋值。

  在本教程,当你下载东西时,可以看到在~/Brochures7文件夹有7个PDF小册子,每个小册子对应一个种类,Seafood除外。我故意没为Seafood提供PDF小册子,以便探讨如何处理某些记录没有附带二进制数据的情况。在服务器资源管理器里右键点击Categories,选“查看表数据”,输入文件路径,如图1所示。由于Seafood类没有图片,将其BrochurePath的值设为“NULL”。 


图1:手工为表Categories的BrochurePath列键入值

第2步:在GridView里添加一个下载链接

  当为表Categories的BrochurePath列赋值后,我们准备创建一个GridView用于展示每个种类,并附带一个链接下载每个类的小册子。在第4步我们将扩展GridView以显示每个类的图片。

  打开BinaryData文件夹的DisplayOrDownloadData.aspx页面并进入设计模式,从工具箱里拖一个GridView控件到页面,设其ID为Categories,从其智能标签选择绑定到一个名为CategoriesDataSource的ObjectDataSource控件。该控件调用类CategoriesBLL的GetCategories()方法。


图2:创建一个名为CategoriesDataSource的ObjectDataSource控件


图3:设置ObjectDataSource使用CategoriesBLL类


图4:调用GetCategories()方法

  完成设置后,Visual Studio自动的为CategoryID, CategoryName, Description, NumberOfProducts和BrochurePath生成BoundField。移除NumberOfProducts,因为GetCategories()方法用不上,同样将CategoryID移除了。分别把CategoryName和 BrochurePath的HeaderText属性改为“Category”和“Brochure”。做上述修改后,你的GridView and ObjectDataSource的声明代码看起来应该像下面的这样:

<asp:GridView ID="Categories" runat="server"
 AutoGenerateColumns="False" DataKeyNames="CategoryID"
 DataSourceID="CategoriesDataSource" EnableViewState="False">
 <Columns>
 <asp:BoundField DataField="CategoryName" HeaderText="Category"
  SortExpression="CategoryName" />
 <asp:BoundField DataField="Description" HeaderText="Description"
  SortExpression="Description" />
 <asp:BoundField DataField="BrochurePath" HeaderText="Brochure"
  SortExpression="BrochurePath" />
 </Columns>
</asp:GridView>

<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
 OldValuesParameterFormatString="original_{0}"
 SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

  在浏览器查看该页(如图5)。列出了所有的8个类,除了Seafood,其它7个类的BoundField列里显示各自的BrochurePath值。由于Seafood的BrochurePath为NULL值,看起来为空格。


图5:显示了每个类别的Name, Description和BrochurePath值

  与其显示BrochurePath的text值,不如创建一个指向小册子的链接。移除BrochurePath,代之以HyperLinkField。设它的HeaderText属性为“Brochure”,Text属性为“View Brochure”, DataNavigateUrlFields属性为“ BrochurePath”。

 


图6:添加一个指向BrochurePath的HyperLinkField

  这样将在GridView里添加一列链接,如图7所示。点“View Brochure”时要么直接在浏览器显示PDF,要么提示用户下载该文件。这取决于浏览器的设置以及是否安装了PDF阅读器。


图7:点击“View Brochure”访问某类的Brochure


图8:显示某类的PDF文件

隐藏无小册子图片的类的“View Brochure” 文本

  如图7所示,不管某个类的BrochurePath是否为NULL值,名为BrochurePath的HyperLinkField都呈现为其Text属性(“View Brochure”) 。当然,如果BrochurePath为NULL值,链接只显示为文本(而不带下划线),就像Seafood类一样(见图7)。与显示文本“View Brochure”相比,更为可取的是将那些BrochurePath值为空的类显示为“No Brochure Available”。

  为达此目的,我们需要用到TemplateField,使其产生一个基于BrochurePath值的合适的结果。我们先来看看如何实现,就像在教程之12《在GridView控件中使用TemplateField》一样。

  在“编辑列”对话框里选中名为BrochurePath的HyperLinkField,再点“Convert this field into a TemplateField”链接,将其转换为TemplateField。 


图9:将HyperLinkField转换为TemplateField

  这样将创建一个TemplateField,其ItemTemplate模板包含一个HyperLink Web控件,该控件的NavigateUrl属性为BrochurePath值。用下面的代码将其替换掉:

<asp:TemplateField HeaderText="Brochure">
 <ItemTemplate>
 <%# GenerateBrochureLink(Eval("BrochurePath")) %>
 </ItemTemplate>
</asp:TemplateField>

  然后,在ASP.NET页面的“后台代码”里添加一个protected类型的GenerateBrochureLink方法,它接受一个输入参数并返回一个字符串。

protected string GenerateBrochureLink(object BrochurePath)
{
 if (Convert.IsDBNull(BrochurePath))
 return "No Brochure Available";
 else
 return string.Format(@"<a href=""{0}"">View Brochure</a>",
  ResolveUrl(BrochurePath.ToString()));
}

  该方法判断传入的值是否为NULL。如果是,则返回一个消息指出该类没有小册子文件;相反,如果传入值不为空,将显示为一个链接。我们注意到,当BrochurePath值不为空时,将调用ResolveUrl(url)方法。该方法的作用在于将传入的相对路径转换为物理路径。比如应用程序的根目录在/Tutorial55,ResolveUrl("~/Brochures/Meats.pdf")返回的路径是/Tutorial55/Brochures/Meat.pdf.

图10为经过上述修改后的界面。我们注意到Seafood类的BrochurePath列现在显示为文本“No Brochure Available”.


图10:没有小册子的类将显示为文本“No Brochure Available”

第3步:新增页面以显示类的图片

  当用户访问一个ASP.NET页面时,他将接收该页面的HTML代码。HTML代码仅仅包含了text文本,而并不包含任何的二进制数据。任何的二进制数据,比如图片,音乐文件、Flash程序、Windows Media Player视频等,以独立资源的形式存放于服务器。

  HTML只包含了这些文件的引用,并不包含这些文件本身。

  比如,在HTML里<img>元素用来引用一张图片,其src属性指向该图片文件,如:
<img src="MyPicture.jpg" ... />

  当浏览器收到HTML代码时,它向服务器发送获取图片的请求并将其显示在浏览器中,该模式对所有的二进制数据都适用。在第2步中,我们没有在页面的HTML标记里将小册子显示在浏览器,而是在HTML标记里提供一个超链接,当点击它是,导致浏览器直接请求PDF文件。

  为了显示或允许用户下载储存在数据库中的二进制数据,我们需要另外创建一个页面,用于从数据库返回所需的数据。对我们的应用程序而言,由于直接存储在数据库中的二进制数据只有一项——类的图片,所以我们需要一个页面,当需要时从数据库返回某个特定类的图片。

  在BinaryData文件夹添加一个DisplayCategoryPicture.aspx页面,注意不要使用母版页。该页面接受一个包含CategoryID值的查询字符串,返回Picture列的二进制数据。由于该页只返回二进制数据,所以我们不需要页面的HTML部分有任何代码。进入页面的“源码”模式,删除页面的所有代码,只保留<%@ Page %>部分。也即:DisplayCategoryPicture.aspx页面的声明代码应该只由如下的单独行构成:

<%@ Page Language="C#" AutoEventWireup="true"
 CodeFile="DisplayCategoryPicture.aspx.cs"
 Inherits="BinaryData_DisplayCategoryPicture" %>

如果<%@ Page %>里包含有MasterPageFile属性,将其删除,同时在后台代码类的Page_Load事件处理器里添加如下代码:

protected void Page_Load(object sender, EventArgs e)
{
 int categoryID = Convert.ToInt32(Request.QueryString["CategoryID"]);

 // Get information about the specified category
 CategoriesBLL categoryAPI = new CategoriesBLL();
 Northwind.CategoriesDataTable categories =
 categoryAPI.GetCategoryWithBinaryDataByCategoryID(categoryID);
 Northwind.CategoriesRow category = categories[0];

 // Output HTTP headers providing information about the binary data
 Response.ContentType = "image/bmp";

 // Output the binary data
 // But first we need to strip out the OLE header
 const int OleHeaderLength = 78;
 int strippedImageLength = category.Picture.Length - OleHeaderLength;
 byte[] strippedImageData = new byte[strippedImageLength];
 Array.Copy(category.Picture, OleHeaderLength,
 strippedImageData, 0, strippedImageLength);

 Response.BinaryWrite(strippedImageData);
}

  代码先读取查询字符串的CategoryID值,并对名为categoryID的变量赋值。然后,通过调用CategoriesBLL类的
GetCategoryWithBinaryDataByCategoryID(categoryID)方法获取图片数据,再通过Response.BinaryWrite(data)方法向客户端返回数据。不过在此之前先要剥离数据的OLE报头。怎么实现呢?创建一个名为strippedImageData的byte数组,它包含的字节刚好比Picture列的数据少78。而Array.Copy方法将从category.Picture的第78个字节开始复制数据(即刚好剥离OLE报头)。

  代码中的Response.ContentType属性指定了要返回内容的MIME type,以便浏览器知道如何显示数据。由于Categories表的Picture列存储的是位图图片,故在这里,位图图片的MIME type是(image/bmp). 如果你忽视了MIME type,绝大多数浏览器也可以正确的显示图像,因为,它们能根据图像文件的二进制数据的内容而推断其类型。即便如此,还是尽可能的使用MIME type。

创建页面后,可以访问页面

DisplayCategoryPicture.aspx?CategoryID=categoryID来查看某个特定类的图片。图11显示的是Beverages类的图片,页面为
DisplayCategoryPicture.aspx?CategoryID=1.


图11:显示类Beverages的图片

  有时候,当你访问DisplayCategoryPicture.aspx?CategoryID=categoryID页面时,有可能显示这样的提示:“Unable to cast object of type 'System.DBNull' to type 'System.Byte[]'”。原因有可能是如下2方面。第一,表Categories的Picture列允许为NULL值,而DisplayCategoryPicture.aspx page页面总是假定传入的为非NULL值。当Picture为NULL值时,不能直接访问CategoriesDataTable的Picture属性。如果你允许Picture为NULL值,添加如下代码:

if (category.IsPictureNull())
{
 // Display some "No Image Available" picture
 Response.Redirect("~/Images/NoPictureAvailable.gif");
}
else
{
 // Send back the binary contents of the Picture column
 // ... Set ContentType property and write out ...
 // ... data via Response.BinaryWrite ...
}

  上述代码假定在Images文件夹里存在名为NoPictureAvailable.gif的图片,当某个类没有图片时,就显示该图片。

  另一种情况:当你在向导里选用“使用SQL语句”的模式再次运行主查询时,它将影响GetCategoryWithBinaryDataByCategoryID方法的SELECT命令返回的列(换句话说,主查询没有返回Picture列,再次运行主查询时将使GetCategoryWithBinaryDataByCategoryID方法也不会返回Picture列)。所以,应确保GetCategoryWithBinaryDataByCategoryID方法的SELECT命令返回Picture列。

  注意:每次访问DisplayCategoryPicture.aspx页面时,都会访问数据库并返回所需的图片。如果图片自最近一次访问以来没有改变过的话,这样每次访问数据库再返回数据的做法效率是不高的。幸运的是,HTTP允许使用conditional GETs,这样的话,客户端使HTTP请求发送一个If-Modified-Since HTTP header。If-Modified-Since HTTP header包含了客户端最近一次从服务器获取的数据以及时间。如果请求的内容没有发生改变,服务器响应为Not Modified status code (304),并不返回请求的内容。简而言之,如果请求的资源自最近一次访问以来没发送改变的话,服务器将不会回传该资源,以达到减轻服务器负荷的目的。

第四步:在GridView控件里显示Category Pictures

  现在我们有一个web页面来显示某个特定种类的图片的。通过Image Web控件或 HTML <img>元素来指向DisplayCategoryPicture.aspx?CategoryID=categoryID页面,从而达到显示该图片的目的。我们可以在GridView控件或DetailsView控件的 ImageField里显示图片。ImageField的DataImageUrlField属性、DataImageUrlFormatString属性与HyperLinkField的DataNavigateUrlFields属性、DataNavigateUrlFormatString属性用法相似。

  让我们对DisplayOrDownloadData.aspx页面里名为Categories的GridView控件进行扩充。添加一个ImageField,设其DataImageUrlField属性为CategoryID;
DataImageUrlFormatString属性为DisplayCategoryPicture.aspx?CategoryID={0}。这样将为GridView增加一列,呈现为一个<img>元素,其src属性为DisplayCategoryPicture.aspx?CategoryID={0},其中{0}将由GridView row的CategoryID值填充。


图12:为GridView控件添加一个ImageField

添加完成后,你的GridView控件的声明代码看起来应像下面这样:

<asp:GridView ID="Categories" runat="server" AutoGenerateColumns="False"
 DataKeyNames="CategoryID" DataSourceID="CategoriesDataSource"
 EnableViewState="False">
 <Columns>
 <asp:BoundField DataField="CategoryName" HeaderText="Category"
  SortExpression="CategoryName" />
 <asp:BoundField DataField="Description" HeaderText="Description"
  SortExpression="Description" />
 <asp:TemplateField HeaderText="Brochure">
  <ItemTemplate>
  <%# GenerateBrochureLink(Eval("BrochurePath")) %>
  </ItemTemplate>
 </asp:TemplateField>
 <asp:ImageField DataImageUrlField="CategoryID"
  DataImageUrlFormatString="DisplayCategoryPicture.aspx?CategoryID={0}">
 </asp:ImageField>
 </Columns>
</asp:GridView>

花几分钟在浏览器里查看该页面,注意每一行记录现在都包含一张该类的图片。


图13:每一行记录都显示一张图片

总结:

  在本节我们探讨了如何显示二进制数据,数据是如何呈现的取决于它的类型。对PDF小册子文件来说,我们提供了一个“View Brochure”链接,当点击它时,直接将用户指向PDF小册子文件。对某个种类的图片,我们先是创建一个页面来从数据库获取并显示它,然后再在一个GridView控件里显示图片。既然看到了如何展示二进制数据,我们准备探讨如何对其展开插入、更新、删除操作。接下来的教程我们看如何将上传文件和相应的数据库记录联系起来。然后,再探讨如何更新现存的二进制数据,以及当删除数据库记录时如何删除相应的二进制数据。

  祝编程快乐!

作者简介

  本系列教程作者 Scott Mitchell,著有六本ASP/ASP.NET方面的书,是4GuysFromRolla.com的创始人,自1998年以来一直应用 微软Web技术。大家可以点击查看全部教程《[翻译]Scott Mitchell 的ASP.NET 2.0数据教程》,希望对大家的学习ASP.NET有所帮助。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索asp.net
, pdf
, 图片
, 二进制
gridview
,以便于您获取更多的相关知识。

时间: 2024-10-31 06:01:32

在ASP.NET 2.0中操作数据之五十三:在Data Web控件显示二进制数据_自学过程的相关文章

在ASP.NET 2.0中操作数据之五十八:在程序启动阶段缓存数据_自学过程

导言: 前面2章考察了在表现层和缓存层缓存数据.在第56章,我们探讨了在表现层设置ObjectDataSource的相关cache属性来缓存数据.在第57章,我们探讨了创建一个单独的分开的缓存层.这2章都是采用"应激装载"(reactive loading)的模式来缓存数据.该模式下,每次请求数据时,系统先检查其是否在内存,如果没有,则从数据源--比如数据库,来获取数据,然后将其存储在内存里.该模式的优势在于执行起来很容易:而缺点之一在于应"请求"(requests

在ASP.NET 2.0中操作数据之二十四:分页和排序报表数据_自学过程

导言 分页和排序是在WEB应用程序中展现数据常见的功能.比如,当我们在一个网上书店搜索ASP.NET书籍的时候,可能有几百本相关书籍,但是我们只希望每页显示10条有效记录.而且,我们还希望结果能根据标题.价格.页数和作者等等来进行排序.过去的23个教程中我们研究了如何建立各种报表,包括在界面上添加编辑和删除数据.但是我们没有研究如何对数据进行排序,对于分页我们也仅在研究DetailsView和FormView控件的时候看到. Step 1:添加分页和排序页面 在我们开始以前,首先让我们花些时间来

在ASP.NET 2.0中操作数据之十三:在DetailsView控件中使用TemplateField_自学过程

导言 比起BoundField.CheckBoxField.HyperLinkField以及其他的那些数据字段控件(data field controls)来说,TemplateField提供了一种高度复杂的数据呈现的方法.在上一节中,我们主要着重于在GridVIew中使用TemplateField,以实现: ·在一列中显示多个数据字段.比如说,将FirstName和LastName字段合并起来显示在一个GridView列中. ·使用交互Web控件来展示数据.我们看到了如何使用一个Calenda

在ASP.NET 2.0中操作数据之五十:为GridView控件添加Checkbox_自学过程

导言: 在前面的教程里我们探讨了如何为GridView控件添加radio buttons列.当用户最多只能选择一项数据时,我们可以在用户界面里添加radio buttons列,而有时候,我们需要选择任意多项数据.比如,基于Web的邮箱客户端列出了一系列的邮件,及一列checkboxes,用户可选择任意封邮件并执行相同的操作,比如移动到另一个文件夹或将之删除. 在本教程,我们将探讨如何添加checkboxes列,以及发生页面回传后如何确定到底选择了哪些checkboxes.特别的,我们将练习一个类

ASP.NET 2.0数据教程之第五十五:在Data Web控件显示二进制数据

返回"ASP.NET 2.0数据教程目录" 导言: 在前面的教程我们阐述了应用程序处理二进制数据的2种模式, 以及使用FileUpload 控件从浏览器向服务器文件系统上传文件.当文件上传并存 储在文件系统里时,应在相应的数据库记录里存储该文件的存储路径. 我 们先来看如何为最终用户提供二进制数据.怎样展示二进制数据呢?这取决于其 类型.比如图片,我们将其显示为image:如果是PDFs,Microsoft Word文档.ZIP 文件或其它类型的数据,或许提供一个"Downl

在ASP.NET 2.0中操作数据之三十八:处理BLL和DAL的异常_自学过程

导言 在DataList里编辑和删除数据概述里,我们创建了一个提供简单编辑和删除功能的DataList.虽然功能上已经完整了,但是对用户来说是不友好的.因为所有在编辑和删除过程中产生的异常都是未处理的.比如,遗漏了输入product的name,或者编辑product时在price里输入"Very affordable!",都会抛出异常.而由于在代码里未捕捉这些异常,页面会显示ASP.NET运行时的详细错误信息. 如我们在在ASP.NET页面中处理BLL/DAL层的异常里看到的,如果BL

在ASP.NET 2.0中操作数据之六十八:为DataTable添加额外的列_自学过程

导言: 当向类型化的数据集(Typed DataSet)添加一个TableAdapter时,相应的DataTable的构架已经由TableAdapter的主查询定义好了.比如,如果主查询返回A, B,C这3个域,那么 DataTable将有对应的3个列A, B,和C.除了主查询以外,TableAdapter还可以包含其他的查询,可能是返回基于某些参数的数据.比如,ProductsTableAdapter的主查询返回所有产品的信息,此外,ProductsTableAdapter还包含诸如GetPr

在ASP.NET 2.0中操作数据之七十一:保护连接字符串及其它设置信息_自学过程

导言: ASP.NET应用程序的设置信息通常都存储在一个名为Web.config的XML文件里.在教程的前面部分我们已经好几次修改过Web.config文件了.比如在第一章,我们创建名为Northwind的数据集时,数据库连接字符串信息自动的添加到Web.config文件的<connectionStrings>节点.再后来,在第3章里,我们手动更新了Web.config文件,添加了一个<pages>元素,对所有的ASP.NET页面运用DataWebControls主题. 由于Web

在ASP.NET 2.0中操作数据之四十七:用SqlDataSource控件插入、更新、删除数据_自学过程

导言: 正如在教程概述插入.更新和删除数据里讨论的那样,GridView控件内置更新和删除功能,而DetailsView和FormView控件不仅具有编辑和删除功能,还有插入功能.我们不要写一行代码就可一将这些功能直接应用于一个数据源控件.在这篇教程里,我们指出ObjectDataSource控件最好与GridView, DetailsView和FormView控件一起使用,才更好的实现插入.更新和删除功能.对SqlDataSource控件来说,同样如此! 对ObjectDataSource控件