新增記錄時包含檔案上傳選項 (C#)

作者 :Scott Mitchell

下載 PDF

本教學課程示範如何建立 Web 介面,讓用戶能夠同時輸入文字數據和上傳二進位檔。 為了說明可用來儲存二進位數據的選項,一個檔案將會儲存在資料庫中,另一個檔案則儲存在文件系統中。

簡介

在先前的兩個教學課程中,我們探索了儲存與應用程式數據模型相關聯之二進位數據的技術、瞭解如何使用 FileUpload 控件將檔案從用戶端傳送至 Web 伺服器,以及瞭解如何在數據 Web 控件中呈現此二進位數據。 不過,我們尚未討論如何將上傳的數據與數據模型產生關聯。

在本教學課程中,我們將建立網頁以新增類別。 除了類別名稱和描述的 TextBox,此頁面還需要包含兩個 FileUpload 控件,一個用於新的類別圖片,另一個用於折頁簿。 上傳的圖片會直接儲存在新記錄的數據 Picture 行中,而折頁簿將會儲存到 ~/Brochures 資料夾,其中儲存了新記錄數據 BrochurePath 行中儲存盤案的路徑。

建立這個新的網頁之前,我們必須更新架構。 s CategoriesTableAdapter main query 不會擷取數據 Picture 行。 因此,自動產生的Insert方法只有、 DescriptionBrochurePath 欄位的CategoryName輸入。 因此,我們需要在 TableAdapter 中建立額外的方法,以提示所有四 Categories 個字段。 商業 CategoriesBLL 規則層中的類別也需要更新。

步驟 1:將方法新增InsertWithPictureCategoriesTableAdapter

當我們在建立數據存取層教學課程中重新建立CategoriesTableAdapter時,我們會將其設定為根據主要查詢自動產生INSERTUPDATEDELETE 語句。 此外,我們指示 TableAdapter 採用 DB Direct 方法,該方法會建立 、 UpdateDelete方法Insert。 這些方法會執行自動產生的 INSERTUPDATEDELETE 語句,因此會根據主查詢所傳回的數據行接受輸入參數。 在 上傳檔案 教學課程中,我們擴充 CategoriesTableAdapter 了使用數據 BrochurePath 行的主要查詢。

CategoriesTableAdapter由於 s 主要查詢不會參考資料Picture行,因此我們無法加入新記錄,也無法使用數據行的值Picture來更新現有的記錄。 為了擷取這項資訊,我們可以在 TableAdapter 中建立新的方法,以特別用來插入具有二進位數據的記錄,或者我們可以自定義自動產生的 INSERT 語句。 自定義自動產生的 INSERT 語句有問題在於,我們可能會讓精靈覆寫自定義專案。 例如,假設我們自定義 INSERT 語句以包含數據行的使用 Picture 。 這會更新 TableAdapter s Insert 方法,以包含類別圖片二進位數據的額外輸入參數。 接著,我們可以在商業規則層中建立方法,以使用此 DAL 方法,並透過簡報層叫用此 BLL 方法,而且一切都能順利運作。 也就是說,直到下一次透過 TableAdapter 組態精靈設定 TableAdapter 為止。 一旦精靈完成,就會覆寫對 語句的 INSERT 自定義、 Insert 方法會還原成舊窗體,而我們的程序代碼將不再編譯!

注意

當使用預存程式而非臨機操作 SQL 語句時,這個不常見的問題。 未來的教學課程將探索使用預存程式,而不是數據存取層中的臨機操作 SQL 語句。

若要避免這種潛在的麻煩,而不是自定義自動產生的 SQL 語句,請改為為 TableAdapter 建立新的方法。 名為InsertWithPicture的這個方法會接受 、DescriptionBrochurePathPicture 數據行的值CategoryName,並執行語句,以將所有四個INSERT值儲存在新記錄中。

開啟 [具類型的數據集],然後從 [Designer],以滑鼠右鍵按鍵按兩下CategoriesTableAdapter標頭,然後從操作功能表選擇 [新增查詢]。 這會啟動 TableAdapter 查詢設定精靈,其開頭是詢問 TableAdapter 查詢應該如何存取資料庫。 選擇 [使用 SQL 語句],然後按 [下一步]。 下一個步驟會提示產生查詢的類型。 因為我們要建立查詢以將新記錄新增至 Categories 數據表,請選擇 [插入],然後按 [下一步]。

選取 INSERT 選項

圖 1:選取 INSERT 選項 (按兩下即可檢視完整大小的影像)

我們現在需要指定 INSERT SQL 語句。 精靈會自動建議 INSERT 對應至 TableAdapter 主要查詢的語句。 在此情況下,它是 INSERT 插入 CategoryNameDescriptionBrochurePath 值的語句。 更新 語句, Picture 讓數據行與 參數一 @Picture 起包含,如下所示:

INSERT INTO [Categories] 
    ([CategoryName], [Description], [BrochurePath], [Picture]) 
VALUES 
    (@CategoryName, @Description, @BrochurePath, @Picture)

精靈的最後一個畫面會要求我們命名新的 TableAdapter 方法。 輸入 InsertWithPicture 並按兩下 [完成]。

將新的 TableAdapter 方法命名為 InsertWithPicture

圖 2:將新的 TableAdapter 方法 InsertWithPicture 命名 (按兩下即可檢視全大小影像)

步驟 2:更新商業規則層

由於表示層應該只與商業規則層介面,而不是略過它直接移至數據存取層,因此我們需要建立 BLL 方法,以叫用我們剛才建立的 DAL 方法 (InsertWithPicture) 。 在本教學課程中,請在名為 InsertWithPicture 的 類別中CategoriesBLL建立方法,以接受做為輸入三 string s 和byte陣列。 輸入 string 參數適用於類別的名稱、描述和折頁簿檔案路徑,而 byte 陣列則適用於類別圖片的二進位內容。 如下列程式代碼所示,此 BLL 方法會叫用對應的 DAL 方法:

[System.ComponentModel.DataObjectMethodAttribute
    (System.ComponentModel.DataObjectMethodType.Insert, false)] 
public void InsertWithPicture(string categoryName, string description, 
    string brochurePath, byte[] picture)
{
    Adapter.InsertWithPicture(categoryName, description, brochurePath, picture);
}

注意

將方法新增 InsertWithPicture 至 BLL 之前,請確定您已儲存具類型的數據集。 由於類別 CategoriesTableAdapter 程式代碼會根據具型別的 DataSet 自動產生,如果您不先將變更儲存至具類型的 DataSet, Adapter 則屬性不會知道 InsertWithPicture 方法。

步驟 3:列出現有類別及其二進位數據

在本教學課程中,我們將建立一個頁面,讓使用者將新的類別新增至系統,並提供新類別的圖片和折頁簿。 在 上述教學課程中 ,我們使用 GridView 搭配 TemplateField 和 ImageField 來顯示每個類別的名稱、描述、圖片,以及下載其折頁簿的連結。 讓我們復寫本教學課程的該功能,建立一個頁面,同時列出所有現有的類別,並允許建立新的類別。

從開啟DisplayOrDownload.aspxBinaryData資料夾的頁面開始。 移至 [來源] 檢視,並複製 GridView 和 ObjectDataSource 的宣告式語法,並將它貼到 中的 <asp:Content>UploadInDetailsView.aspx元素內。 此外,別忘了將 方法從 的程式代碼後置類別DisplayOrDownload.aspxUploadInDetailsView.aspx複製到 GenerateBrochureLink

將宣告式語法從DisplayOrDownload.aspx複製並貼到UploadInDetailsView.aspx

圖 3:將宣告式語法從 DisplayOrDownload.aspx 複製並貼到 UploadInDetailsView.aspx (按鍵即可檢視完整大小的影像)

將宣告式語法和 GenerateBrochureLink 方法 UploadInDetailsView.aspx 複製到頁面之後,請透過瀏覽器檢視頁面,以確保所有專案都已正確複製。 您應該會看到 GridView 列出包含下載折頁冊的連結以及類別圖片的八個類別。

您現在應該會看到每個類別及其二進位數據

圖 4:您現在應該會看到每個類別及其二進位數據 (按兩下以檢視全大小影像)

步驟 4:設定CategoriesDataSource以支援插入

CategoriesDataSource GridView 所使用的 Categories ObjectDataSource 目前不提供插入數據的能力。 為了支援透過這個數據源控件插入,我們需要將其Insert方法對應至其基礎物件中的方法。 CategoriesBLL 特別是,我們想要將它對應至CategoriesBLL我們在步驟 2 中新增的方法。 InsertWithPicture

從從 ObjectDataSource 智慧標記按兩下 [設定資料源] 連結開始。 第一個畫面顯示資料來源設定為使用的物件。 CategoriesBLL 依原樣保留此設定,然後按 [下一步] 前進到 [定義數據方法] 畫面。 移至 [INSERT] 索引標籤,然後從下拉式清單中挑選 InsertWithPicture 方法。 按一下 [完成] 以完成精靈。

設定 ObjectDataSource 以使用 InsertWithPicture 方法

圖 5:將 ObjectDataSource 設定為使用 InsertWithPicture 方法 (按鍵即可檢視完整大小的影像)

注意

完成精靈時,Visual Studio 可能會詢問您是否要重新整理字段和索引鍵,這會重新產生數據 Web 控件欄位。 選擇 [否],因為選擇 [是] 會覆寫您可能所做的任何字段自定義。

完成精靈之後,ObjectDataSource 現在會包含其 InsertMethod 屬性的值,以及 InsertParameters 四個類別數據行的值,如下列宣告式標記所示:

<asp:ObjectDataSource ID="CategoriesDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}" SelectMethod="GetCategories" 
    TypeName="CategoriesBLL" InsertMethod="InsertWithPicture">
    <InsertParameters>
        <asp:Parameter Name="categoryName" Type="String" />
        <asp:Parameter Name="description" Type="String" />
        <asp:Parameter Name="brochurePath" Type="String" />
        <asp:Parameter Name="picture" Type="Object" />
    </InsertParameters>
</asp:ObjectDataSource>

步驟 5:建立插入介面

插入、更新和刪除數據概觀中所述,DetailsView 控件提供內建的插入介面,可在使用支援插入的數據源控件時使用。 讓我們在 GridView 上方新增 DetailsView 控件,以永久轉譯其插入介面,讓使用者快速新增類別。 在 DetailsView 中新增類別時,其下方的 GridView 會自動重新整理並顯示新的類別。

首先,將 DetailsView 從 [工具箱] 拖曳到 GridView 上方的 Designer,並將其 ID 屬性設定為 NewCategory ,並清除 HeightWidth 屬性值。 從 DetailsView 的智慧標記中,將它系結至現有的 CategoriesDataSource ,然後核取 [啟用插入] 複選框。

顯示 [DetailsView] 開啟的螢幕快照,其中已將 CategoryID 屬性設定為 NewCategory、空白 Height 和 Width 屬性值,並已選取 [啟用插入] 複選框。

圖 6:將 DetailsView 系結至 CategoriesDataSource 和 [啟用插入] (按兩下即可檢視完整大小的影像)

若要在其插入介面中永久轉譯 DetailsView,請將其 DefaultMode 屬性設定為 Insert

請注意,DetailsView 有五個 BoundFields CategoryID、、DescriptionNumberOfProductsBrochurePathCategoryName雖然 CategoryID BoundField 不會在插入介面中轉譯,因為它InsertVisible的 屬性設定為 false。 這些 BoundField 存在,因為它們是方法所 GetCategories() 傳回的數據行,這是 ObjectDataSource 叫用以擷取其數據的數據行。 不過,為了插入,我們不想讓使用者指定的值 NumberOfProducts。 此外,我們需要讓他們上傳新類別的圖片,以及上傳折頁簿的 PDF。

NumberOfProducts從 DetailsView 移除 BoundField,然後將 和 BrochurePath BoundFields 的屬性CategoryName分別更新HeaderText為 Category 和 Brochure。 接下來,將 BrochurePath BoundField 轉換成 TemplateField,併為圖片新增 TemplateField,讓這個新的 TemplateField HeaderText 值為 Picture。 Picture移動TemplateField,使其位於TemplateField與 CommandField 之間BrochurePath

[欄位] 視窗的螢幕快照,其中已醒目提示 TemplateField、Picture 和 HeaderText。

圖 7:將 DetailsView 系結至 CategoriesDataSource 並啟用插入

如果您透過 [編輯欄位] 對話框將 BrochurePath BoundField 轉換成 TemplateField,則 TemplateField 包含 ItemTemplateEditItemTemplateInsertItemTemplateInsertItemTemplate不過,只需要 ,因此請隨意移除其他兩個範本。 此時,DetailsView 的宣告式語法看起來應該如下所示:

<asp:DetailsView ID="NewCategory" runat="server" AutoGenerateRows="False" 
    DataKeyNames="CategoryID" DataSourceID="CategoriesDataSource" 
    DefaultMode="Insert">
    <Fields>
        <asp:BoundField DataField="CategoryID" HeaderText="CategoryID" 
            InsertVisible="False" ReadOnly="True" 
            SortExpression="CategoryID" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category" 
            SortExpression="CategoryName" />
        <asp:BoundField DataField="Description" HeaderText="Description" 
            SortExpression="Description" />
        <asp:TemplateField HeaderText="Brochure" SortExpression="BrochurePath">
            <InsertItemTemplate>
                <asp:TextBox ID="TextBox1" runat="server"
                    Text='<%# Bind("BrochurePath") %>'></asp:TextBox>
            </InsertItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Picture"></asp:TemplateField>
        <asp:CommandField ShowInsertButton="True" />
    </Fields>
</asp:DetailsView>

新增 [折頁簿] 和 [圖片字段] 的 FileUpload 控件

BrochurePath目前,TemplateField s InsertItemTemplate 包含 TextBox,而 Picture TemplateField 不包含任何範本。 我們需要更新這兩個 TemplateField s InsertItemTemplate 以使用 FileUpload 控件。

從 DetailsView 的智慧標記中,選擇 [編輯範本] 選項,然後從下拉式清單中選取 BrochurePath TemplateField s InsertItemTemplate 。 拿掉 TextBox,然後將 FileUpload 控件從 [工具箱] 拖曳到範本中。 將 FileUpload 控制項設定 IDBrochureUpload。 同樣地,將 FileUpload 控制項新增至 Picture TemplateField s InsertItemTemplate。 將此 FileUpload 控制項設定 IDPictureUpload

將 FileUpload 控件新增至 InsertItemTemplate

圖 8:將 FileUpload 控件新增至 InsertItemTemplate (按兩下即可檢視完整大小的影像)

進行這些新增之後,兩個 TemplateField 的宣告式語法將會是:

<asp:TemplateField HeaderText="Brochure" SortExpression="BrochurePath">
    <InsertItemTemplate>
        <asp:FileUpload ID="BrochureUpload" runat="server" />
    </InsertItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Picture">
    <InsertItemTemplate>
        <asp:FileUpload ID="PictureUpload" runat="server" />
    </InsertItemTemplate>
</asp:TemplateField>

當使用者新增類別時,我們想要確保折頁簿和圖片是正確的文件類型。 針對摺頁冊,用戶必須提供 PDF。 針對圖片,我們需要用戶上傳圖像檔案,但是否允許 任何 圖像檔或只允許特定類型的圖像檔,例如 GIF 或 JPG? 為了允許不同的檔類型,我們需要擴充 Categories 架構,以包含擷取檔類型的數據行,讓此類型可以透過 Response.ContentType 傳送 DisplayCategoryPicture.aspx至用戶端。 因為我們沒有這類數據行,因此最好限制使用者只提供特定的圖像文件類型。 數據表 Categories 現有的影像是位圖,但 JPG 是網頁上提供影像的更適當的檔格式。

如果用戶上傳不正確的檔類型,我們需要取消插入並顯示指出問題的訊息。 在 DetailsView 下方新增標籤 Web 控件。 將 ID 屬性設定為 UploadWarning、清除其 Text 屬性、將 CssClass 屬性設定為 Warning,並將 VisibleEnableViewState 屬性設定為 falseWarning CSS 類別定義於 中Styles.css,並以大、紅色、斜體、粗體字型呈現文字。

注意

在理想情況下, CategoryNameDescription BoundFields 會轉換成 TemplateFields 及其自定義的插入介面。 Description例如,插入介面可能更適合透過多行文本框。 由於數據 CategoryName 行不接受 NULL 值,因此應該新增 RequiredFieldValidator,以確保使用者提供新類別名稱的值。 這些步驟會保留為讀者的練習。 如需增強數據修改介面的深入探討,請參閱 自定義數據修改介面

步驟 6:將上傳的折頁簿儲存至網頁伺服器文件系統

當使用者輸入新類別的值並按下 [插入] 按鈕時,就會發生回傳,並展開插入工作流程。 首先,DetailsView s ItemInserting 事件 會引發。 接下來,會叫用 ObjectDataSource s Insert() 方法,這會導致新記錄新增至 Categories 數據表。 之後,DetailsView s ItemInserted 事件 就會引發。

在叫用 ObjectDataSource s Insert() 方法之前,我們必須先確定使用者已上傳適當的檔類型,然後將折頁簿 PDF 儲存至網頁伺服器的文件系統。 建立 DetailsView s ItemInserting 事件的事件處理程式,並新增下列程式代碼:

// Reference the FileUpload control
FileUpload BrochureUpload = 
    (FileUpload)NewCategory.FindControl("BrochureUpload");
if (BrochureUpload.HasFile)
{
    // Make sure that a PDF has been uploaded
    if (string.Compare(System.IO.Path.GetExtension
        (BrochureUpload.FileName), ".pdf", true) != 0)
    {
        UploadWarning.Text = 
            "Only PDF documents may be used for a category's brochure.";
        UploadWarning.Visible = true;
        e.Cancel = true;
        return;
    }
}

事件處理程式會從 DetailsView 的範本參考 BrochureUpload FileUpload 控件開始。 然後,如果已上傳折頁冊,則會檢查上傳的擴展名。 如果延伸模組未 .PDF,則會顯示警告、取消插入,並結束事件處理程序的執行。

注意

依賴上傳的檔案擴展名並不是確保上傳的檔案是 PDF 文件的確定引發技術。 使用者可能具有擴展名的有效 PDF 檔 .Brochure,或可能已取得非 PDF 檔,並將它指定為 .pdf 延伸模組。 檔案的二進位內容必須以程序設計方式檢查,才能更明確地驗證文件類型。 不過,這類徹底的方法通常超額;檢查擴充功能就足以用於大部分的案例。

上傳檔案 教學課程所述,將檔案儲存至文件系統時,請務必小心,如此一來,用戶上傳就不會覆寫另一個檔案。 在本教學課程中,我們將嘗試使用與上傳檔案相同的名稱。 不過,如果目錄中已有同一個檔名的檔案 ~/Brochures ,我們會在結尾附加數位,直到找到唯一的名稱為止。 例如,如果使用者上傳名為 的 Meats.pdf折頁簿檔案,但資料夾中已經有名為 Meats.pdf 的檔案,我們會將儲存的 ~/Brochures 檔案名變更為 Meats-1.pdf。 如果存在,我們會嘗試 Meats-2.pdf等,直到找到唯一的檔名為止。

下列程式代碼會 File.Exists(path) 使用 方法來 判斷檔案是否已存在具有指定檔名。 如果是的話,它會繼續嘗試新檔名供摺頁冊使用,直到找不到衝突為止。

const string BrochureDirectory = "~/Brochures/";
string brochurePath = BrochureDirectory + BrochureUpload.FileName;
string fileNameWithoutExtension = 
    System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName);
int iteration = 1;
while (System.IO.File.Exists(Server.MapPath(brochurePath)))
{
    brochurePath = string.Concat(BrochureDirectory, 
        fileNameWithoutExtension, "-", iteration, ".pdf");
    iteration++;
}

找到有效的檔名之後,檔案必須儲存至文件系統,且 ObjectDataSource 的值 brochurePath``InsertParameter 必須更新,以便將此檔名寫入資料庫。 如我們在 上傳檔案 教學課程中所見,可以使用 FileUpload 控件 s SaveAs(path) 方法儲存盤案。 若要更新 ObjectDataSource s brochurePath 參數,請使用 e.Values 集合。

// Save the file to disk and set the value of the brochurePath parameter
BrochureUpload.SaveAs(Server.MapPath(brochurePath));
e.Values["brochurePath"] = brochurePath;

步驟 7:將上傳的圖片儲存至資料庫

若要將上傳的圖片儲存在新記錄中,我們必須將上傳的 Categories 二進位內容指派給 DetailsView s 事件中的 ObjectDataSource s pictureItemInserting 參數。 不過,在進行此指派之前,我們必須先確定上傳的圖片是 JPG,而不是其他影像類型。 如同步驟 6,讓我們使用上傳的圖片擴展名來確認其類型。

Categories雖然數據表允許NULL數據Picture行的值,但所有類別目前都有圖片。 讓我們強制使用者在透過此頁面新增類別時提供圖片。 下列程式代碼會檢查,以確保圖片已上傳,且具有適當的擴充功能。

// Reference the FileUpload controls
FileUpload PictureUpload = (FileUpload)NewCategory.FindControl("PictureUpload");
if (PictureUpload.HasFile)
{
    // Make sure that a JPG has been uploaded
    if (string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), 
            ".jpg", true) != 0 &&
        string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), 
            ".jpeg", true) != 0)
    {
        UploadWarning.Text = 
            "Only JPG documents may be used for a category's picture.";
        UploadWarning.Visible = true;
        e.Cancel = true;
        return;
    }
}
else
{
    // No picture uploaded!
    UploadWarning.Text = 
        "You must provide a picture for the new category.";
    UploadWarning.Visible = true;
    e.Cancel = true;
    return;
}

此程式代碼應該放在步驟 6 的程式代碼 之前 ,如此一來,如果圖片上傳發生問題,事件處理程式將會在折頁簿檔案儲存到文件系統之前終止。

假設已上傳適當的檔案,請使用下列程式代碼行,將上傳的二進位內容指派給圖片參數的值:

// Set the value of the picture parameter
e.Values["picture"] = PictureUpload.FileBytes;

完整ItemInserting事件處理程式

為了完整起見,以下是 ItemInserting 整個事件處理程式:

protected void NewCategory_ItemInserting(object sender, DetailsViewInsertEventArgs e)
{
    // Reference the FileUpload controls
    FileUpload PictureUpload = (FileUpload)NewCategory.FindControl("PictureUpload");
    if (PictureUpload.HasFile)
    {
        // Make sure that a JPG has been uploaded
        if (string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), 
                ".jpg", true) != 0 &&
            string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), 
                ".jpeg", true) != 0)
        {
            UploadWarning.Text = 
                "Only JPG documents may be used for a category's picture.";
            UploadWarning.Visible = true;
            e.Cancel = true;
            return;
        }
    }
    else
    {
        // No picture uploaded!
        UploadWarning.Text = 
            "You must provide a picture for the new category.";
        UploadWarning.Visible = true;
        e.Cancel = true;
        return;
    }
    // Set the value of the picture parameter
    e.Values["picture"] = PictureUpload.FileBytes;
    
    
    // Reference the FileUpload controls
    FileUpload BrochureUpload = 
        (FileUpload)NewCategory.FindControl("BrochureUpload");
    if (BrochureUpload.HasFile)
    {
        // Make sure that a PDF has been uploaded
        if (string.Compare(System.IO.Path.GetExtension(BrochureUpload.FileName), 
            ".pdf", true) != 0)
        {
            UploadWarning.Text = 
                "Only PDF documents may be used for a category's brochure.";
            UploadWarning.Visible = true;
            e.Cancel = true;
            return;
        }
        const string BrochureDirectory = "~/Brochures/";
        string brochurePath = BrochureDirectory + BrochureUpload.FileName;
        string fileNameWithoutExtension = 
            System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName);
        int iteration = 1;
        while (System.IO.File.Exists(Server.MapPath(brochurePath)))
        {
            brochurePath = string.Concat(BrochureDirectory, fileNameWithoutExtension, 
                "-", iteration, ".pdf");
            iteration++;
        }
        // Save the file to disk and set the value of the brochurePath parameter
        BrochureUpload.SaveAs(Server.MapPath(brochurePath));
        e.Values["brochurePath"] = brochurePath;
    }
}

步驟 8:修正DisplayCategoryPicture.aspx頁面

讓我們花點時間測試過去幾個步驟所建立的插入介面和 ItemInserting 事件處理程式。 UploadInDetailsView.aspx瀏覽網頁並嘗試新增類別,但省略圖片,或指定非 JPG 圖片或非 PDF 折頁簿。 在這些情況下,將會顯示錯誤訊息,並取消插入工作流程。

如果上傳無效的檔類型,則會顯示警告訊息

圖 9:如果上傳無效的檔類型 (按兩下即可檢視大小完整的影像 ,則會顯示警告訊息)

一旦您確認頁面需要上傳圖片,且不接受非 PDF 或非 JPG 檔案,請新增具有有效 JPG 圖片的新類別,讓 [折頁簿] 字段空白。 按兩下 [插入] 按鈕之後,頁面會回傳,並將新的記錄新增至數據表, Categories 其中包含直接儲存在資料庫中的已上傳影像二進位內容。 GridView 會更新並顯示新新增類別的數據列,但如圖 10 所示,新類別的圖片不會正確轉譯。

[新增類別] 的圖片未顯示

圖 10:新類別的圖片未顯示 (按下即可檢視大小完整的影像)

未顯示新圖片的原因是傳 DisplayCategoryPicture.aspx 回指定類別圖片的頁面已設定為處理具有 OLE 標頭的點陣圖。 這個 78 位元組標頭會先從 Picture 數據行的二進位內容中移除,再將它們傳回用戶端。 但我們剛針對新類別上傳的 JPG 檔案沒有這個 OLE 標頭;因此,從影像二進位數據中移除有效的必要位元組。

由於現在數據表中有兩個位圖具有 OLE 標頭和 JPG Categories ,因此我們需要更新 DisplayCategoryPicture.aspx ,讓它執行原始八個類別的 OLE 標頭等量,並略過較新類別記錄的這個等量分割。 在下一個教學課程中,我們將檢查如何更新現有的記錄影像,並將更新所有舊的類別圖片,使其為 JPG。 不過,現在,請使用 中的 DisplayCategoryPicture.aspx 下列程式代碼,只針對這些原始八個類別來移除 OLE 標頭:

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];
    if (categoryID <= 8)
    {
        // For older categories, we must strip the OLE header... images are bitmaps
        // 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);
    }
    else
    {
        // For new categories, images are JPGs...
        
        // Output HTTP headers providing information about the binary data
        Response.ContentType = "image/jpeg";
        // Output the binary data
        Response.BinaryWrite(category.Picture);
    }
}

透過這項變更,JPG 影像現在會在 GridView 中正確轉譯。

新類別的 JPG 影像已正確轉譯

圖 11:新類別的 JPG 影像已正確轉譯 (按兩下即可檢視完整大小的影像)

步驟 9:刪除例外狀況臉部中的折頁冊

在網頁伺服器文件系統上儲存二進位數據的挑戰之一,就是它引進了數據模型與其二進位數據之間的中斷連線。 因此,每當刪除記錄時,也必須移除檔系統上的對應二進位數據。 這也會在插入時生效。 請考慮下列案例:使用者新增類別,並指定有效的圖片和折頁簿。 按兩下 [插入] 按鈕時,就會發生回傳,並引發DetailsView s ItemInserting 事件,並將折頁簿儲存到網頁伺服器的文件系統。 接下來,會叫用 ObjectDataSource s Insert() 方法,它會呼叫 類別 s InsertWithPicture 方法,以呼叫 CategoriesBLLCategoriesTableAdapter s InsertWithPicture 方法。

現在,如果資料庫離線,或 SQL 語句中有 INSERT 錯誤,會發生什麼事? 很明顯地,INSERT 將會失敗,因此不會將新的類別數據列新增至資料庫。 但我們仍然有位於網頁伺服器文件系統上的已上傳折頁簿檔案! 此檔案必須在插入工作流程期間,在發生例外狀況時刪除。

如先前在 ASP.NET Page 教學課程 中處理 BLL 和 DAL-Level 例外 狀況中所述,當例外狀況從架構的深度擲回時,它會在各種層級中反升。 在呈現層中,我們可以判斷 DetailsView s ItemInserted 事件是否發生例外狀況。 這個事件處理程式也會提供 ObjectDataSource s InsertParameters的值。 因此,我們可以為 ItemInserted 事件建立事件處理程式,以檢查是否有例外狀況,如果是,則會刪除 ObjectDataSource s brochurePath 參數所指定的檔案:

protected void NewCategory_ItemInserted
    (object sender, DetailsViewInsertedEventArgs e)
{
    if (e.Exception != null)
    {
        // Need to delete brochure file, if it exists
        if (e.Values["brochurePath"] != null)
            System.IO.File.Delete(Server.MapPath(
                e.Values["brochurePath"].ToString()));
    }
}

摘要

必須執行許多步驟,才能提供 Web 介面,以新增包含二進位數據的記錄。 如果二進位數據直接儲存至資料庫,您可能需要更新架構,新增特定方法來處理插入二進位數據的情況。 架構更新之後,下一個步驟是建立插入介面,這可以使用已自定義的 DetailsView 來完成,以包含每個二進位數據欄位的 FileUpload 控件。 然後,上傳的數據可以儲存至網頁伺服器的文件系統,或指派給 DetailsView ItemInserting 事件處理程式中的數據源參數。

將二進位數據儲存至檔案系統需要比將數據直接儲存到資料庫還要多。 必須選擇命名配置,以避免某個用戶上傳覆寫另一個。 此外,如果資料庫插入失敗,則必須採取額外的步驟來刪除上傳的檔案。

我們現在能夠使用折頁簿和圖片,將新的類別新增至系統,但我們尚未查看如何更新現有類別的二進位數據,或如何正確移除已刪除類別的二進位數據。 我們將在下一個教學課程中探索這兩個主題。

快樂的程序設計!

關於作者

Scott Mitchell 是七份 ASP/ASP.NET 書籍的作者,以及 1998 年以來與 Microsoft Web 技術合作的 4GuysFromRolla.com 作者。 Scott 是獨立顧問、訓練員和作者。 他的最新書籍是 Sams 在 24 小時內自行 ASP.NET 2.0。 您可以透過mitchell@4GuysFromRolla.com部落格來連線到 ,您可以在 找到http://ScottOnWriting.NET

特別感謝

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