当前位置: 移动技术网 > IT编程>开发语言>.net > 在ASP.NET 2.0中操作数据之五十三:在Data Web控件显示二进制数据

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

2017年12月12日  | 移动技术网IT编程  | 我要评论

林志炫无字神曲,赣南师范学院地址,入伍前夕

导言:

  在前面的教程我们阐述了应用程序处理二进制数据的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”。 

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908392490.jpg
图1:手工为表categories的brochurepath列键入值

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

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

  打开binarydata文件夹的displayordownloaddata.aspx页面并进入设计模式,从工具箱里拖一个gridview控件到页面,设其id为categories,从其智能标签选择绑定到一个名为categoriesdatasource的objectdatasource控件。该控件调用类categoriesbll的getcategories()方法。

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908396032.jpg
图2:创建一个名为categoriesdatasource的objectdatasource控件

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908407034.jpg
图3:设置objectdatasource使用categoriesbll类

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908409104.jpg
图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值,看起来为空格。

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908401106.jpg
图5:显示了每个类别的name, description和brochurepath值

  与其显示brochurepath的text值,不如创建一个指向小册子的链接。移除brochurepath,代之以hyperlinkfield。设它的headertext属性为“brochure”,text属性为“view brochure”, datanavigateurlfields属性为“ brochurepath”。

 

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908404933.jpg
图6:添加一个指向brochurepath的hyperlinkfield

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

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908405934.jpg
图7:点击“view brochure”访问某类的brochure

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908418005.jpg
图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。 

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908419006.jpg
图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”.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908415306.jpg
图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.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908414835.jpg
图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值填充。

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908421135.jpg
图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>

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

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908427907.jpg
图13:每一行记录都显示一张图片

总结:

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

  祝编程快乐!

作者简介

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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网