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

作者:Scott Mitchell

下載 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 行。 在下一個教學課程中,我們將瞭解如何完成這項作業,但現在我們需要手動提供此數據行的值。

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

手動輸入 Category 數據表的 BrochurePath 數據行值

圖 1:手動輸入資料表數據BrochurePath行的值Categories, (按兩下即可檢視大小完整的影像)

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

首先,將 GridView 從 [工具箱] 拖曳到資料夾中頁面BinaryData的 DesignerDisplayOrDownloadData.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 新增至 CategoriesCategoryNameDescriptionNumberOfProductsBrochurePathDataColumn 的 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值,會顯示空的儲存格。

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

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

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

新增適用於 BrochurePath 的 HyperLinkField

圖 6:為 新增 HyperLinkField BrochurePath

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

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

圖 7:單擊 [檢視折頁冊] 連結 (按兩下即可檢視 類別的折頁冊)

[類別] 的 [摺頁冊 PDF] 隨即顯示

圖 8:類別目錄的 [折頁冊 PDF] 會顯示 (按兩下即可檢視大小完整的影像)

隱藏沒有折頁簿之類別的檢視折頁簿文字

如圖 7 所示, BrochurePath HyperLinkField 會針對所有記錄顯示其 Text 屬性值 ( 檢視折頁冊 ) ,不論 是否有非NULLBrochurePath。 當然,如果 是 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 只是文字,不包含任何二進位數據。 任何其他二進位數據,例如影像、音效檔案、宏媒體 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.gifImages些名為 的影像檔,您想要針對沒有圖片的類別顯示。

如果 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 包含 DataImageUrlFieldDataImageUrlFormatString 屬性,其運作方式類似 HyperLinkField s DataNavigateUrlFieldsDataNavigateUrlFormatString 屬性。

讓我們藉由新增 ImageField 來顯示每個類別的圖片來增強 Categories GridView DisplayOrDownloadData.aspx 。 只要將 ImageField 新增,並將其 DataImageUrlFieldDataImageUrlFormatString 屬性分別設定為 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 中顯示每個類別的圖片。

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

快樂的程序設計!

關於作者

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。