以資料 Web 控制項顯示二進位資料 (C#)
在本教學課程中,我們將探討在網頁上呈現二進位數據的選項,包括影像檔案的顯示,以及 PDF 檔案的「下載」連結布建。
簡介
在上述教學課程中,我們探索了將二進位數據與應用程式基礎數據模型產生關聯,並使用 FileUpload 控件將檔案從瀏覽器上傳至網頁伺服器檔系統的兩種技術。 我們尚未瞭解如何將上傳的二進位數據與數據模型產生關聯。 也就是說,在檔案上傳並儲存至文件系統之後,檔案的路徑必須儲存在適當的資料庫記錄中。 如果數據直接儲存在資料庫中,則上傳的二進位數據不需要儲存至文件系統,但必須插入資料庫。
不過,在查看將數據與數據模型產生關聯之前,讓我們先看看如何將二進位數據提供給使用者。 呈現文字數據很簡單,但應該如何呈現二進位數據? 當然,這取決於二進位數據的類型。 對於影像,我們可能會想要顯示影像;對於 PDF、Microsoft Word 檔、ZIP 檔案和其他類型的二進位數據,提供下載連結可能更適合。
在本教學課程中,我們將探討如何使用 GridView 和 DetailsView 等數據 Web 控件,將二進位數據與其相關聯的文字數據一起呈現。 在下一個教學課程中,我們將注意將上傳的檔案與資料庫產生關聯。
步驟 1:提供BrochurePath
值
Picture
數據表中的數據Categories
行已經包含各種類別影像的二進位數據。 具體來說, Picture
每筆記錄的數據行都會保留粒度、低品質、16 色位圖影像的二進位內容。 每個類別影像寬度為 172 圖元,高度為 120 像素,耗用約 11 KB。 此外,數據行中的 Picture
二進位內容包含必須移除 78 位元組 的 OLE 標頭,才能顯示影像。 此標頭資訊存在,因為 Northwind 資料庫在 Microsoft Access 中有其根目錄。 在 Access 中,二進位數據會使用 OLE Object 數據類型來儲存,而此數據類型會在此標頭上攔截。 現在,我們將瞭解如何從這些低品質影像中移除標頭,以顯示圖片。 在未來的教學課程中,我們將建置介面來更新類別數據行, Picture
並將這些位圖影像取代為對等的 JPG 影像,而不需要不必要的 OLE 標頭。
在上述教學課程中,我們已瞭解如何使用 FileUpload 控件。 因此,您可以繼續將折頁簿檔案新增至網頁伺服器的文件系統。 不過,這樣做不會更新 BrochurePath
數據表中的數據 Categories
行。 在下一個教學課程中,我們將瞭解如何完成這項作業,但現在我們需要手動提供此數據行的值。
在本教學課程中,您會在資料夾中找到七個 PDF 折頁簿檔案 ~/Brochures
,每個類別的一個檔案除外。 我有目的地省略了新增一份「檔案」折頁簿,以說明如何處理並非所有記錄都有相關聯的二進位數據的情況。 若要使用這些值更新 Categories
數據表,請以滑鼠右鍵按兩下 [伺服器總管] 中的 Categories
節點,然後選擇 [顯示資料表數據]。 然後,針對每個具有摺頁冊的類別,輸入摺頁冊檔案的虛擬路徑,如圖 1 所示。 因為沒有 [存貨] 類別的摺頁冊,所以將其 BrochurePath
資料行的值保留為 NULL
。
圖 1:手動輸入資料表數據BrochurePath
行的值Categories
, (按兩下即可檢視大小完整的影像)
步驟 2:為 GridView 中的折頁簿提供下載連結
BrochurePath
有了為Categories
數據表提供的值,我們就可以建立 GridView 來列出每個類別以及下載類別的折頁簿連結。 在步驟 4 中,我們將擴充此 GridView,以同時顯示類別影像。
首先,將 GridView 從 [工具箱] 拖曳到資料夾中頁面BinaryData
的 DesignerDisplayOrDownloadData.aspx
。 將 GridView 設定 ID
為 Categories
,並透過 GridView 的智慧標記,選擇將其系結至新的數據源。 具體而言,請將它系結至名為 CategoriesDataSource
的 ObjectDataSource,以使用 CategoriesBLL
物件 s GetCategories()
方法擷取數據。
圖 2:建立名為 CategoriesDataSource
的新 ObjectDataSource (按兩下即可檢視大小完整的映射)
圖 3:將 ObjectDataSource 設定為使用 CategoriesBLL
類別 (按兩下即可檢視大小完整的影像)
圖 4:使用 GetCategories()
方法擷取類別清單 (按兩下以檢視大小完整的影像)
完成 [設定數據源精靈] 之後,Visual Studio 會自動將 BoundField 新增至 Categories
、 CategoryName
Description
、 NumberOfProducts
和 BrochurePath
DataColumn
的 GridViewCategoryID
。 繼續並移除 NumberOfProducts
BoundField,因為 GetCategories()
方法的查詢不會擷取這項資訊。 同時移除 CategoryID
BoundField, CategoryName
並將 和 BrochurePath
BoundFields HeaderText
屬性分別重新命名為 Category 和 Brochure。 進行這些變更之後,您的 GridView 和 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) 。 列出八個類別的每一個。 具有值的七個 BrochurePath
類別具有 BrochurePath
個別 BoundField 中顯示的值。 NULL
內含其 BrochurePath
值,會顯示空的儲存格。
圖 5:每個類別的名稱、描述和 BrochurePath
值都會列出 (按兩下即可檢視大小完整的影像)
我們想要建立摺頁冊的連結,而不是顯示欄的 BrochurePath
文字。 若要達成此目的,請移除 BrochurePath
BoundField,並將它取代為 HyperLinkField。 將新的 HyperLinkField s HeaderText
屬性設定為 Brochure,其 Text
屬性設定為 View Brochure,並將其 DataNavigateUrlFields
屬性設定為 BrochurePath
。
圖 6:為 新增 HyperLinkField BrochurePath
這會新增 GridView 鏈接的數據行,如圖 7 所示。 按兩下 [檢視折頁簿] 連結會直接在瀏覽器中顯示 PDF,或提示使用者下載檔案,視 PDF 閱讀程式是否已安裝及瀏覽器的設定而定。
圖 7:單擊 [檢視折頁冊] 連結 (按兩下即可檢視 類別的折頁冊)
圖 8:類別目錄的 [折頁冊 PDF] 會顯示 (按兩下即可檢視大小完整的影像)
隱藏沒有折頁簿之類別的檢視折頁簿文字
如圖 7 所示, BrochurePath
HyperLinkField 會針對所有記錄顯示其 Text
屬性值 ( 檢視折頁冊 ) ,不論 是否有非NULL
值 BrochurePath
。 當然,如果 是 BrochurePath
NULL
,則連結只會以文字顯示,如同使用 [ (] 類別 (回到圖 7) 。 相較於顯示 [檢視折頁簿] 文字,如果沒有值顯示一些替代文字,例如 [沒有折頁簿可用],可能很適合使用這些類別 BrochurePath
。
為了提供此行為,我們需要使用TemplateField,其內容是透過呼叫頁面方法產生,以根據 BrochurePath
值發出適當的輸出。 我們先在 GridView 控件中使用 TemplateFields 教學課程中探索此格式設定技術。
選取 BrochurePath
[HyperLinkField],然後按兩下 [編輯數據行] 對話方塊中的 [將此字段轉換成 TemplateField] 連結,將 HyperLinkField 轉換成 TemplateField。
圖 9:將 HyperLinkField 轉換成 TemplateField
這會建立 TemplateField,其中包含ItemTemplate
其屬性系結至BrochurePath
值的 HyperLink Web 控制件NavigateUrl
。 將這個標記取代為 方法 GenerateBrochureLink
的呼叫,並傳入 的值 BrochurePath
:
<asp:TemplateField HeaderText="Brochure">
<ItemTemplate>
<%# GenerateBrochureLink(Eval("BrochurePath")) %>
</ItemTemplate>
</asp:TemplateField>
接下來,在名為 GenerateBrochureLink
的 ASP.NET 頁程式代碼後置類別中建立protected
方法,以傳回 string
,並接受 object
做為輸入參數。
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()));
}
這個方法會判斷傳入 object
的值是否為資料庫 NULL
,如果是,則傳回訊息,指出類別缺少折頁冊。 否則,如果有 BrochurePath
值,則會顯示在超連結中。 請注意,如果 BrochurePath
值存在,則會傳入 ResolveUrl(url)
方法。 此方法會解析傳入 的 URL,並將 ~
字元取代為適當的虛擬路徑。 例如,如果應用程式位於 /Tutorial55
的根目錄, ResolveUrl("~/Brochures/Meats.pdf")
將會傳回 /Tutorial55/Brochures/Meat.pdf
。
圖 10 顯示套用這些變更之後的頁面。 請注意,[檔案類別目錄] BrochurePath
字段現在會顯示 [沒有可用的摺頁冊] 文字。
圖 10:這些類別的文字沒有可用的折頁簿, (按兩下即可檢視全尺寸影像)
步驟 3:新增網頁以顯示類別圖片
當使用者流覽 ASP.NET 頁面時,他們會收到 ASP.NET 頁面的 HTML。 收到的 HTML 只是文字,不包含任何二進位數據。 任何其他二進位數據,例如影像、音效檔案、宏媒體 Flash 應用程式、內嵌 Windows 媒體播放器 影片等等,都以網頁伺服器上的個別資源的形式存在。 HTML 包含這些檔案的參考,但不包含檔案的實際內容。
例如,在 HTML 中, <img>
元素用來參考圖片,而 src
屬性指向圖像檔,如下所示:
<img src="MyPicture.jpg" ... />
當瀏覽器收到此 HTML 時,它會向網頁伺服器提出另一個要求,以擷取影像檔案的二進位內容,然後顯示在瀏覽器中。 相同的概念適用於任何二進位數據。 在步驟 2 中,折頁簿不會在網頁 HTML 標記中向下傳送到瀏覽器。 相反地,已轉譯的 HTML 提供的超連結在按兩下時,會導致瀏覽器直接要求 PDF 檔。
若要顯示或允許使用者下載位於資料庫中的二進位數據,我們必須建立傳回數據的個別網頁。 針對我們的應用程式,只有一個二進位數據欄位會直接儲存在類別的圖片資料庫中。 因此,我們需要呼叫的頁面,傳回特定類別的影像數據。
將新的 ASP.NET 頁面新增至 BinaryData
名為 DisplayCategoryPicture.aspx
的資料夾。 這樣做時,請取消核取 [選取主版頁面] 複選框。 此頁面需要 CategoryID
查詢字串中的值,並傳回該類別數據 Picture
行的二進位數據。 由於此頁面會傳回二進位數據,因此不需要 HTML 區段中的任何標記。 因此,按兩下角的 [來源] 索引標籤,並移除指示詞以外的 <%@ Page %>
所有頁面標記。 也就是說, DisplayCategoryPicture.aspx
宣告式標記應該包含單行:
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="DisplayCategoryPicture.aspx.cs"
Inherits="BinaryData_DisplayCategoryPicture" %>
如果您在 MasterPageFile
指示詞中看到 <%@ Page %>
屬性,請將其移除。
在頁面的程式代碼後置類別中,將下列程式代碼新增至 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);
}
此程式代碼會從將 querystring 值讀取 CategoryID
到名為的 categoryID
變數開始。 接下來,會透過呼叫 CategoriesBLL
類別 s GetCategoryWithBinaryDataByCategoryID(categoryID)
方法來擷取圖片數據。 此數據會使用 Response.BinaryWrite(data)
方法傳回給用戶端,但在呼叫此數據之前, Picture
必須移除數據行值 s OLE 標頭。 這是藉由建立 byte
名為 的 strippedImageData
陣列來完成,此陣列會精確保留小於數據行中的 Picture
字元 78 個字元。 方法是Array.Copy
用來將資料從category.Picture
位置 78 開始複製到 strippedImageData
。
屬性 Response.ContentType
會指定要傳回內容的 MIME類型 ,讓瀏覽器知道如何轉譯它。 Categories
因為數據表的數據Picture
行是位圖影像,所以位圖MIME類型會在這裡使用, (image/bmp) 。 如果您省略MIME類型,大部分的瀏覽器仍會正確顯示影像,因為它們可以根據圖像檔二進位資料的內容來推斷類型。 不過,最好盡可能包含MIME類型。 如需MIME媒體類型的完整清單,請參閱因特網指派號碼授權單位網站。
建立此頁面之後,您可以瀏覽 DisplayCategoryPicture.aspx?CategoryID=categoryID
來檢視特定類別的圖片。 圖 11 顯示可從 檢視 DisplayCategoryPicture.aspx?CategoryID=1
的 [飲料] 類別圖片。
圖 11:按兩下即可 檢視全大小影像) 顯示 [圖片] (顯示
如果造訪 DisplayCategoryPicture.aspx?CategoryID=categoryID
時,您會收到一個例外狀況,該例外狀況會讀取無法將類型 'System.DBNull' 的物件轉換成類型 'System.Byte[]',可能會造成此情況的兩件事。 首先, Categories
數據表的數據 Picture
行允許 NULL
值。 DisplayCategoryPicture.aspx
不過,頁面假設有非NULL
值存在。 Picture
如果 屬性CategoriesDataTable
具有NULL
值,則無法直接存取 的屬性。 如果您要允許 NULL
資料列的值 Picture
,您要包含下列條件:
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 ...
}
上述程式代碼假設資料夾中有一NoPictureAvailable.gif
Images
些名為 的影像檔,您想要針對沒有圖片的類別顯示。
如果 CategoriesTableAdapter
s GetCategoryWithBinaryDataByCategoryID
方法 s SELECT
語句已還原回主要查詢數據行清單,則也可能造成這個例外狀況,如果您使用臨機操作 SQL 語句,而且您已針對 TableAdapter 主要查詢重新執行精靈,就會發生這種情況。 檢查以確定 GetCategoryWithBinaryDataByCategoryID
方法 s SELECT
語句仍然包含 資料 Picture
行。
注意
每次造訪 時 DisplayCategoryPicture.aspx
,都會存取資料庫,並傳回指定的類別圖片數據。 如果使用者上次檢視過該圖片之後,類別的圖片尚未變更,但這會浪費心力。 幸運的是,HTTP 允許 條件式 GET。 透過條件式 GET,發出 HTTP 要求的用戶端會沿著 If-Modified-Since
HTTP 標頭 傳送,以提供用戶端上次從網頁伺服器擷取此資源的日期和時間。 如果內容自此指定日期后尚未變更,網頁伺服器可能會以 [未修改] 狀態代碼 (304) 回應,並放棄傳送要求的資源內容。 簡言之,這項技術可減輕網頁伺服器在用戶端上次存取后尚未修改資源的內容時,不必傳送回內容。
不過,若要實作此行為,您必須將數據行加入 PictureLastModified
數據表, Categories
以擷取上次更新數據行的時間 Picture
,以及檢查 If-Modified-Since
標頭的程序代碼。 如需標頭和條件式 GET 工作流程的詳細資訊 If-Modified-Since
,請參閱 HTTP 條件式 GET for RSS 駭客 和 更深入的查看在 ASP.NET 頁面中執行 HTTP 要求。
步驟 4:在 GridView 中顯示類別圖片
既然我們有一個網頁來顯示特定類別圖片,我們可以使用 影像 Web 控件 或指向的 HTML <img>
元素來 DisplayCategoryPicture.aspx?CategoryID=categoryID
顯示它。 URL 由資料庫數據決定的影像,可以使用 ImageField 顯示在 GridView 或 DetailsView 中。 ImageField 包含 DataImageUrlField
和 DataImageUrlFormatString
屬性,其運作方式類似 HyperLinkField s DataNavigateUrlFields
和 DataNavigateUrlFormatString
屬性。
讓我們藉由新增 ImageField 來顯示每個類別的圖片來增強 Categories
GridView DisplayOrDownloadData.aspx
。 只要將 ImageField 新增,並將其 DataImageUrlField
和 DataImageUrlFormatString
屬性分別設定為 CategoryID
和 DisplayCategoryPicture.aspx?CategoryID={0}
。 這會建立 GridView 數據行來轉譯<img>
其屬性參考 DisplayCategoryPicture.aspx?CategoryID={0}
的專案,其中 {0} 會取代為 GridView 數據列的值CategoryID
src
。
圖 12:將 ImageField 新增至 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 折頁簿檔案,我們提供用戶檢視折頁簿連結,按兩下時,會直接將使用者帶至 PDF 檔案。 針對類別圖片,我們先建立頁面來擷取和傳回資料庫中的二進位數據,然後使用該頁面在 GridView 中顯示每個類別的圖片。
既然我們已瞭解如何顯示二進位數據,我們就可以檢查如何使用二進位數據對資料庫執行插入、更新和刪除。 在下一個教學課程中,我們將探討如何將上傳的檔案與其對應的資料庫記錄產生關聯。 在本教學課程之後,我們將瞭解如何更新現有的二進位數據,以及如何在移除其相關聯的記錄時刪除二進位數據。
快樂的程序設計!
關於作者
Scott Mitchell 是 1998 年以來,1998 年與 Microsoft Web 技術合作的 七篇 ASP/ASP.NET 書籍和 4GuysFromRolla.com 作者。 Scott 是獨立的顧問、訓練者和作者。 他的最新書籍是 Sams 在 24 小時內自行 ASP.NET 2.0。 您可以透過mitchell@4GuysFromRolla.com部落格連到,也可以透過其部落格來存取,網址為 http://ScottOnWriting.NET。
特別感謝
本教學課程系列是由許多實用的檢閱者所檢閱。 本教學課程的首席檢閱者是 Teresa Murphy 和 Dave Gardner。 想要檢閱即將推出的 MSDN 文章嗎? 如果是,請將一行放在 mitchell@4GuysFromRolla.com。
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應