共用方式為


以資料 Web 控制項顯示二進位資料 (C#)

由斯科特· 米切爾

下載 PDF

在本教學課程中,我們將探討在網頁上呈現二進位數據的選項,包括顯示圖像檔,以及布建 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 行。 在下一個教學課程中,我們將瞭解如何完成這項作業,但現在我們需要手動提供此數據行的值。

在本教學課程的下載中,您會在 ~/Brochures 資料夾中找到七個 PDF 折頁冊檔案,每個類別各有一個,但「海鮮」除外。 我故意省略了新增海鮮摺頁冊,以說明如何處理並非所有記錄都有相關聯二進位數據的案例。 若要以這些值更新 Categories 數據表,請以滑鼠右鍵按兩下 [伺服器總管] 中的 Categories 節點,然後選擇 [顯示資料表數據]。 然後,輸入每一個類別的摺頁冊檔案的虛擬路徑,其中包含一個摺頁冊,如圖 1 所示。 由於 [海鮮] 類別沒有摺頁冊,因此請將其 BrochurePath 資料行的值保留為 NULL

手動輸入 Categories 數據表 s BrochurePath 數據行的值

圖 1:手動輸入資料表資料BrochurePath列的值Categories按兩下以檢視完整大小的影像

BrochurePath有了數據表Categories所提供的值,我們就可以建立 GridView,列出每個類別以及下載類別折頁冊的連結。 在步驟 4 中,我們將擴充此 GridView,以同時顯示類別的影像。

從 [工具箱] 將 GridView 拖曳至資料夾中頁面BinaryData的設計DisplayOrDownloadData.aspx工具開始。 將 GridView 設定 IDCategories ,並透過 GridView 的智慧標記,選擇將其系結至新的數據源。 具體來說,將它系結至名為 CategoriesDataSource 的 ObjectDataSource,該物件會使用 CategoriesBLL 物件 s GetCategories() 方法來擷取數據。

建立名為 CategoriesDataSource 的新 ObjectDataSource

圖 2:建立名為 CategoriesDataSource 的新 ObjectDataSource (按兩下以檢視完整大小的影像

將 ObjectDataSource 設定為使用 CategoriesBLL 類別

圖 3:將 ObjectDataSource 設定為使用 CategoriesBLL 類別 (按一下即可檢視完整大小的影像

使用 GetCategories() 方法擷取類別清單

圖 4:使用 GetCategories() 方法擷取類別清單 (按兩下以檢視完整大小的影像

完成 [設定數據源精靈] 之後,Visual Studio 會自動將 BoundField 新增至 Categories 、、 DescriptionNumberOfProductsDataColumn BrochurePath 的 GridViewCategoryIDCategoryName。 繼續並移除 NumberOfProducts BoundField,因為 GetCategories() 方法的查詢不會擷取此資訊。 同時移除 CategoryID BoundField,並將 和 BrochurePath BoundFields HeaderText 屬性分別重新命名CategoryName為 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 中顯示的值。 海鮮具有 NULLBrochurePath值,會顯示一個空的單元格。

列出每個類別的名稱、描述和 BrochurePath 值

圖 5:列出每個類別的名稱、描述和 BrochurePath 值(按兩下以檢視完整大小的影像

我們想要建立摺頁冊的連結,而不是顯示數據行的 BrochurePath 文字。 若要達成此目的,請移除 BrochurePath BoundField,並將它取代為 HyperLinkField。 將新的 HyperLinkField 屬性 HeaderText 設定為 Brochure,將其 Text 屬性設定為 View Brochure,並將其 DataNavigateUrlFields 屬性設定為 BrochurePath

新增適用於 BrochurePath 的 HyperLinkField

圖 6:為 新增 HyperLinkField BrochurePath

這會新增 GridView 鏈接的數據行,如圖 7 所示。 按兩下 [檢視折頁冊] 連結會直接在瀏覽器中顯示 PDF,或提示使用者下載檔案,視是否已安裝 PDF 閱讀程式及瀏覽器設定而定。

按兩下 [檢視折頁冊] 連結即可檢視類別的折頁冊

圖 7:單擊 [檢視折頁冊] 連結即可檢視類別的摺頁冊 (按兩下以檢視完整大小的影像

類別的摺頁冊 PDF 隨即顯示

圖 8:類別目錄的折頁冊 PDF 隨即顯示(按兩下以檢視完整大小的影像

隱藏沒有摺頁冊之類別的檢視手冊文字

如圖 7 所示,BrochurePathHyperLinkField 會顯示所有記錄的Text屬性值(檢視摺頁冊),不論 是否有非NULL值。BrochurePath 當然,如果 BrochurePathNULL,則連結只會顯示為文字,如同海鮮類別的情況(請參閱圖 7)。 與其顯示 [檢視摺頁冊] 文字,不如讓那些類別沒有 BrochurePath 值就能顯示一些替代文字,例如 [沒有可用的折頁冊]。

為了提供此行為,我們需要使用TemplateField,其內容是透過呼叫頁面方法產生,以根據 BrochurePath 值發出適當的輸出。 我們先在 GridView 控件教學課程中使用 TemplateFields 來探索此格式技術。

選取 BrochurePath [HyperLinkField],然後按兩下 [編輯數據行] 對話方塊中的 [將此字段轉換成 TemplateField] 連結,將 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 只是文字,而且不包含任何二進位數據。 任何其他二進位數據,例如影像、聲音檔案、宏媒體快閃應用程式、內嵌 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 必須移除數據行值 OLE 標頭。 建立名為 strippedImageData 的陣列,比數據行中的Picture字元少 78 個字元,來完成這項作業byte。 方法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如果 具有 NULL 值,則無法直接存取 的 CategoriesDataTable 屬性。 如果您想要允許 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 控件或指向 DisplayCategoryPicture.aspx?CategoryID=categoryID的 HTML <img> 元素來顯示它 URL 是由資料庫數據決定的影像,可以使用 ImageField 在 GridView 或 DetailsView 中顯示。 ImageField 包含 DataImageUrlFieldDataImageUrlFormatString 屬性,其運作類似 HyperLinkField 和DataNavigateUrlFieldsDataNavigateUrlFormatString屬性。

讓我們藉由新增 ImageField 來顯示每個類別的圖片,以增強 Categories GridView DisplayOrDownloadData.aspx 。 只要新增 ImageField,並將其 和 DataImageUrlField DataImageUrlFormatString 屬性分別設定為 CategoryIDDisplayCategoryPicture.aspx?CategoryID={0}。 這會建立 GridView 數據行,以轉譯<img>其屬性參考 DisplayCategoryPicture.aspx?CategoryID={0}的專案,其中 {0} 會取代為 GridView 數據列的值CategoryIDsrc

將 ImageField 新增至 GridView

圖 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 中顯示每個類別的圖片。

既然我們已瞭解如何顯示二進位數據,我們就可以檢查如何使用二進位數據對資料庫執行插入、更新和刪除。 在下一個教學課程中,我們將探討如何將上傳的檔案與其對應的資料庫記錄產生關聯。 在此之後的教學課程中,我們將瞭解如何更新現有的二進位數據,以及如何在移除其相關聯的記錄時刪除二進位數據。

快樂的程序設計!

關於作者

斯科特·米切爾,七本 ASP/ASP.NET 書籍和 4GuysFromRolla.com 創始人的作者,自1998年以來一直與Microsoft Web 技術合作。 斯科特擔任獨立顧問、教練和作家。 他的最新書是 山姆斯在24小時內 ASP.NET 2.0。 他可以到達 mitchell@4GuysFromRolla.com, 或通過他的博客,可以在 找到 http://ScottOnWriting.NET

特別感謝

本教學課程系列已由許多實用的檢閱者檢閱。 本教學課程的主要檢閱者是 Teresa Murphy 和 Dave Gardner。 有興趣檢閱我即將推出的 MSDN 文章嗎? 如果是,請將一行 mitchell@4GuysFromRolla.com放在 。