濮阳杆衣贸易有限公司

主頁(yè) > 知識(shí)庫(kù) > 在ASP.NET 2.0中操作數(shù)據(jù)之五十四:添加新記錄時(shí)包含一個(gè)文件上傳選項(xiàng)

在ASP.NET 2.0中操作數(shù)據(jù)之五十四:添加新記錄時(shí)包含一個(gè)文件上傳選項(xiàng)

熱門標(biāo)簽:地圖標(biāo)注軟件免費(fèi)下載 聯(lián)通官網(wǎng)400電話辦理 400電話辦理怎么樣 外呼電話機(jī)器人成本 網(wǎng)絡(luò)電話外呼系統(tǒng)上海 臨沂智能電話機(jī)器人加盟 蘇州如何辦理400電話 百應(yīng)電話機(jī)器人外呼系統(tǒng) 西寧呼叫中心外呼系統(tǒng)線路商

導(dǎo)言:

  在前面2節(jié)教程,我們探討了如何使用FileUpload控件從客戶端向服務(wù)器上傳文件,以及如何在數(shù)據(jù)Web控件里顯示二進(jìn)制數(shù)據(jù)。

  在本節(jié),我們將創(chuàng)建一個(gè)web頁(yè)面以添加新的種類。除了為類的name和description屬性添加TextBoxes控件外,我們還要在頁(yè)面上添加2個(gè)FileUpload控件——一個(gè)用來(lái)上傳新類的圖片,另一個(gè)用來(lái)上傳類的小說(shuō)明冊(cè)子。上傳的圖片將直接存儲(chǔ)在新記錄的Picture列。與此相反,小冊(cè)子將存儲(chǔ)在~/Brochures 文件夾,同時(shí)將文件路徑存儲(chǔ)在新記錄的BrochurePath列。

  在創(chuàng)建頁(yè)面之前,我們需要更新體系結(jié)構(gòu)。由于CategoriesTableAdapter的主查詢并不返回Picture列,因此自動(dòng)生產(chǎn)的Insert方法只包含了CategoryName, Description和BrochurePath列。我們需要在TableAdapter里創(chuàng)建新的方法以包括Categories的4個(gè)列。同時(shí)業(yè)務(wù)邏輯層的的CategoriesBLL類也需要更新。

第1步:在CategoriesTableAdapter添加一個(gè)InsertWithPicture方法

  在前面的教程《創(chuàng)建一個(gè)數(shù)據(jù)訪問(wèn)層》里我們創(chuàng)建了CategoriesTableAdapter,并設(shè)置其自動(dòng)生成了基于主查詢的INSERT, UPDATE和DELETE命令。此外,我們?cè)O(shè)置該TableAdapter啟用DB Direct方法,它將創(chuàng)建Insert, Update和Delete方法。這些方法執(zhí)行自動(dòng)生成的INSERT, UPDATE和DELETE命令,自然而然的,其接受的輸入?yún)?shù)基于主查詢所返回的那些列。在教程《使用FileUpload上傳文件》里,我們擴(kuò)展了 CategoriesTableAdapter的主查詢以包含BrochurePath列。

  因?yàn)镃ategoriesTableAdapter的主查詢并為引用Picture,在添加新記錄或更新記錄時(shí)不能涉及Picture值。為了獲取Picture信息,我們要么在TableAdapter里創(chuàng)建一個(gè)新方法以插入Picture的二進(jìn)制數(shù)據(jù);要么定制自動(dòng)生成的INSERT命令。但定制自動(dòng)生成的INSERT命令有一個(gè)風(fēng)險(xiǎn),即定制的INSERT命令有可能被向?qū)Ц采w。比如,假設(shè)我們定制INSERT命令使用Picture列,更新TableAdapter的Insert方法,使之多包含一個(gè)對(duì)應(yīng)picture二進(jìn)制數(shù)據(jù)的參數(shù)。然后在業(yè)務(wù)邏輯層創(chuàng)建一個(gè)方法使用該 DAL方法,再在表現(xiàn)層調(diào)用該業(yè)務(wù)邏輯層方法?,F(xiàn)在一切工作正常,但當(dāng)下一次在TableAdapter設(shè)置向?qū)Ю镌O(shè)置TableAdapter完成后,我們定制的INSERT命令馬上就會(huì)被向?qū)е貙?,回歸到定制前的狀態(tài)。其結(jié)果是我們的代碼將無(wú)法編譯!

  注意:如果使用存儲(chǔ)過(guò)程而不用SQL語(yǔ)句的話,就不存在這個(gè)問(wèn)題。在以后的教程里,我們將探討在數(shù)據(jù)訪問(wèn)層用存儲(chǔ)過(guò)程替代SQL語(yǔ)句。

  為避免這個(gè)頭痛的問(wèn)題,我們?yōu)門ableAdapter添加新的方法,而不定制自動(dòng)生成的SQL命令。我們?yōu)樘砑拥姆椒麨镮nsertWithPicture,它接受 CategoryName, Description, BrochurePath和Picture值;執(zhí)行INSERT命令將上述值添加進(jìn)一條記錄。

  在CategoriesTableAdapter的頂部點(diǎn)右鍵,選擇“添加查詢”。進(jìn)入TableAdapter 查詢?cè)O(shè)置向?qū)?,首先詢?wèn)我們TableAdapter查詢?nèi)绾卧L問(wèn)數(shù)據(jù)庫(kù),選擇“使用SQL語(yǔ)句”,點(diǎn)Next,因?yàn)槲覀円獮楸鞢ategories添加新記錄,選“INSERT”,點(diǎn)Next。


圖1:選“INSERT”選項(xiàng)

  現(xiàn)在,我們需要指定INSERT SQL語(yǔ)句。向?qū)ё詣?dòng)地生成一個(gè)基于主查詢的INSERT語(yǔ)句。此時(shí),它只插入CategoryName, Description和BrochurePath值。對(duì)其更新,包括Picture列和參數(shù)@Picture ,如下:

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

最后,向?qū)б覀優(yōu)榉椒?,取名為InsertWithPicture,點(diǎn)Finish。


圖2:為新方法命名為InsertWithPicture 

第2步:更新業(yè)務(wù)邏輯層

  由于一般來(lái)說(shuō)表現(xiàn)層將引用業(yè)務(wù)邏輯層,而不是繞過(guò)它直接引用數(shù)據(jù)訪問(wèn)層,我們需要?jiǎng)?chuàng)建一個(gè)業(yè)務(wù)邏輯層方法,以調(diào)用剛才創(chuàng)建的數(shù)據(jù)訪問(wèn)層方法(InsertWithPicture),本節(jié),我們?cè)贑ategoriesBLL里創(chuàng)建一個(gè)名為InsertWithPicture方法,它接受3個(gè)字符串和一個(gè)byte數(shù)組,字符串參數(shù)對(duì)應(yīng)name, description和brochure文件地址;byte數(shù)組對(duì)應(yīng)于圖片的二進(jìn)制內(nèi)容。就像下面的代碼所顯示的那樣,BLL方法調(diào)用相應(yīng)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);
}

  注意:在為BLL添加InsertWithPicture方法前,確保已經(jīng)保存了數(shù)據(jù)集(Typed DataSet ),因?yàn)镃ategoriesTableAdapter類的代碼是基于Typed DataSet自動(dòng)生成的。如果最開始沒有把對(duì)Typed DataSet所進(jìn)行的修改保存的話,Adapter屬性將不認(rèn)同InsertWithPicture方法。

第3步:列出現(xiàn)有的種類及其二進(jìn)制數(shù)據(jù)

  本教程我們將創(chuàng)建一個(gè)頁(yè)面,允許用戶添加新的類,包含其圖片和說(shuō)明小冊(cè)子。在上一節(jié),我們用一個(gè)包含TemplateField和ImageField的GridView控件來(lái)展示每個(gè)類的名稱、描述,并包含一個(gè)下載說(shuō)明小冊(cè)子的鏈接。在本教程,我們實(shí)現(xiàn)相同的功能,創(chuàng)建一個(gè)頁(yè)面,即展示現(xiàn)有的類,還可以添加新的類。

  打開BinaryData文件夾的DisplayOrDownload.aspx頁(yè)面,切換到源模式,復(fù)制GridView和ObjectDataSource控件的聲明代碼,粘貼在UploadInDetailsView.aspx頁(yè)面的asp:Content>元素里。同時(shí)不要忘記將后臺(tái)代碼類的GenerateBrochureLink方法拷貝到UploadInDetailsView.aspx的后臺(tái)代碼類里。


圖3:將DisplayOrDownload.aspx頁(yè)面的聲明代碼拷貝到頁(yè)面UploadInDetailsView.aspx

  完成以后,在瀏覽器里查看該頁(yè)面,確保一切正常。GridView控件里列出了8個(gè)類,每個(gè)類包含一張圖片以及一個(gè)下載說(shuō)明小冊(cè)子的鏈接。


圖4:你應(yīng)該看到每個(gè)類及其相應(yīng)二進(jìn)制數(shù)據(jù)

第4步:設(shè)置CategoriesDataSource以支持添加功能

  那個(gè)ID為Categories的GridView控件所使用的名為CategoriesDataSource的 ObjectDataSource控件目前還不支持添加數(shù)據(jù)。為實(shí)現(xiàn)該功能,我們要設(shè)置該控件的Insert方法引用類CategoriesBLL的某個(gè)方法。具體的講,我們要用到在第2步里添加的InsertWithPicture方法。

  在ObjectDataSource控件的智能標(biāo)簽里,點(diǎn)“設(shè)置數(shù)據(jù)源”。照原樣一直點(diǎn)到“Define Data Methods”界面。再點(diǎn)INSERT標(biāo)簽,從下拉列表里選方法“InsertWithPicture”,點(diǎn)Finish完成設(shè)置。


圖5:設(shè)置ObjectDataSource控件使用InsertWithPicture方法

  注意:當(dāng)完成設(shè)置后,Visual Studio會(huì)問(wèn)你是否“刷新Fields and Keys”,選擇No,因?yàn)槿绻xYes的話,將重新構(gòu)造data Web controls fields,那樣將重寫所有我們已經(jīng)定制好的列(field)。

完成設(shè)置后,ObjectDataSource控件將會(huì)為InsertMethod屬性賦值,同時(shí)包含一個(gè)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步:創(chuàng)建一個(gè)插入界面

  在教程16《概述插入、更新和刪除數(shù)據(jù)》里我們談到,當(dāng)DetailsView控件的數(shù)據(jù)源控件支持添加功能時(shí),便可以啟用DetailsView內(nèi)置的添加界面。讓我們?cè)陧?yè)面上添加一個(gè)DetailsView控件,置于GridView控件之上,并處于添加模式。當(dāng)在DetailsView控件里添加一個(gè)新種類時(shí),其下的GridView控件將自動(dòng)發(fā)生刷新,并將剛添加的類顯示出來(lái)。

  從工具箱拖一個(gè)DetailsView控件到頁(yè)面,置于GridView之上,設(shè)其ID為NewCategory,清空其Height和Width屬性。 其智能標(biāo)簽里,設(shè)置它綁定到名為CategoriesDataSource的數(shù)據(jù)源,并啟用“插入”功能。


圖6:將DetailsView控件綁定到CategoriesDataSource,并啟用插入功能。

  為使DetailsView呈現(xiàn)為插入界面,設(shè)其DefaultMode屬性為Insert

  我們注意到,盡管DetailsView控件有5個(gè)BoundFields——CategoryID, CategoryName, Description, NumberOfProducts和BrochurePath,但插入界面并不包含CategoryID,因?yàn)镃ategoryID列的InsertVisible屬性為false。為什么會(huì)顯示這4個(gè)列呢?因?yàn)镺bjectDataSource調(diào)用的GetCategories()方法返回的就是這些列。當(dāng)添加新類時(shí),我們不希望用戶為NumberOfProducts列指定值,此外,我們還希望讓用戶為新類上傳圖片和相關(guān)的PDF小冊(cè)子。

  在DetailsView里將NumberOfProducts列完成刪除,再分別CategoryName列和BrochurePath列的HeaderText屬性設(shè)置為“Category”和“Brochure”。將BrochurePath 轉(zhuǎn)換為TemplateField,再添加一個(gè)TemplateField,設(shè)其HeaderText屬性為“Picture”,把它放置在BrochurePath列和CommandField列之間。


圖7:將DetailsView控件綁定到CategoriesDataSource,并啟用插入功能(注:圖片說(shuō)明有誤)

  當(dāng)你在“編輯列”對(duì)話框里將BrochurePath BoundField 轉(zhuǎn)換為一個(gè)TemplateField后,該TemplateField將包含3個(gè)模板:ItemTemplate,EditItemTemplate和InsertItemTemplate,由于我們只需要InsertItemTemplate模板,將另外2個(gè)模板刪除。如此,你的DetailsView控件的聲明代碼看起來(lái)應(yīng)該像下面的這樣:

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>

為Brochure和Picture Fields添加FileUpload控件

  當(dāng)前,BrochurePath TemplateField的InsertItemTemplate模板包含一個(gè)TextBox,而Picture TemplateField并不包含任何的模板,我們?yōu)檫@2個(gè)TemplateField的InsertItemTemplate模板模板添加FileUpload控件。

  從DetailsView控件的智能標(biāo)簽選擇“編輯模板”,從下拉列表選擇BrochurePath TemplateField的InsertItemTemplate模板,將模板里的TextBox刪除,從工具箱拖一個(gè)FileUpload控件到頁(yè)面,設(shè)其ID為BrochureUpload。類似的,為Picture TemplateField的InsertItemTemplate模板添加一個(gè)ID為PictureUpload的FileUpload控件。


圖8:在InsertItemTemplate模板里添加一個(gè)FileUpload控件

完成添加后,這2個(gè)TemplateField的聲明代碼應(yīng)該和下面的差不多:

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>

  當(dāng)用戶添加一個(gè)新類時(shí),我們希望確保上傳的圖片和說(shuō)明小冊(cè)子是恰當(dāng)?shù)奈募愋?。?duì)說(shuō)明小冊(cè)子,必須是PDF類型;對(duì)圖片,我們需要用戶上傳一個(gè)image文件。那是不是image文件必須是某個(gè)特定的類型呢,比如GIF或JPG?考慮到其它不同類型的文件,我們需要擴(kuò)展表Categories的列以包含這些類型的文件,同時(shí)我們可以在頁(yè)面DisplayCategoryPicture.aspx里通過(guò)Response.ContentType將這些文件發(fā)送到客戶端。由于表Categories現(xiàn)在并沒有這樣的列,我們只有限制用戶上傳指定為某種類型的image文件。表Categories里現(xiàn)有的images為位圖,不過(guò)使用JPG類型或許更恰當(dāng)。

  當(dāng)用戶上傳的文件類型不正確時(shí),我們將取消插入操作,并顯示一個(gè)提示信息。在DetailsView控件下添加一個(gè)Label Web控件,設(shè)ID為UploadWarning,清除Text屬性,設(shè)CssClass屬性為“Warning”, 再將Visible和EnableViewState屬性都設(shè)為false。Warning CSS定義在Styles.css里,作用是將文字顯示為粗斜體,紅色大號(hào)字。

  注意:最理想的情況是將CategoryName和Description BoundFields都轉(zhuǎn)換為TemplateFields,達(dá)到定制插入界面的目的。比如,對(duì)Description插入界面來(lái)說(shuō),使用一個(gè)允許分行的文本框或許更好;對(duì)CategoryName插入界面,因?yàn)镃ategoryName不允許為NULL值,我們應(yīng)該添加一個(gè)RequiredFieldValidator控件,以確保輸入類的名稱。這些步驟都留給讀者做練習(xí),更深入的探討請(qǐng)參考前面的教程之20《定制數(shù)據(jù)修改界面》

第6步:將上傳的小冊(cè)子保存在服務(wù)器的文件系統(tǒng)

  但用戶鍵入相關(guān)的類別信息,點(diǎn)Insert按鈕后,發(fā)生頁(yè)面回傳,接著發(fā)生一連串的插入流程。首先,DetailsView控件的ItemInserting event事件發(fā)生;接著,調(diào)用ObjectDataSource控件的Insert()方法,它將導(dǎo)致Categories表添加新記錄;最后,發(fā)生DetailsView控件的ItemInserted event事件。

  在調(diào)用ObjectDataSource控件的Insert()方法以前,我們必須確保用戶已經(jīng)上傳了恰當(dāng)?shù)奈募⒈4嬖诜?wù)器的文件系統(tǒng)。為此,我們?yōu)镈etailsView控件的ItemInserting事件創(chuàng)建一個(gè)事件處理器,添加如下的代碼:

// 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控件,如果已經(jīng)上傳了文件,就檢查FileUpload控件的extension是否為“.PDF”, 如果不是則取消插入操作并退出。

  注意:通過(guò)檢查文件的擴(kuò)展名(extension)來(lái)確保用戶上傳的為PDF文件的做法并不是萬(wàn)全之策。比如,可能用戶的確上傳的是PDF文件,只不過(guò)其擴(kuò)展名為.Brochure;或者用戶提供的并不是PDF文件,卻使用.pdf的擴(kuò)展名。保險(xiǎn)的做法是通過(guò)編程對(duì)文件內(nèi)容做最后一次檢查。如此一來(lái),雖然徹底,但稍嫌過(guò)頭(overkill)。在絕大多數(shù)情況下,檢查文件擴(kuò)展名就已經(jīng)足夠了。

  就像在教程《使用FileUpload上傳文件》里討論的那樣,將文件保存在文件系統(tǒng)里時(shí)要特別小心,以免覆蓋別人上傳的文件。本節(jié),我們嘗試對(duì)上傳文件使用一個(gè)已經(jīng)使用的名字,在名字末尾添加一個(gè)數(shù)字,以示區(qū)別。舉例,如果在文件夾~/Brochures里存在一個(gè)名為Meats.pdf的文件,上傳文件時(shí)我們?nèi)∶麨镸eats-1.pdf,如果文件夾里恰好也存在一個(gè)Meats-1.pdf文件,我們就取名為Meats-2.pdf,以此類推,直到文件名唯一為止。

下面的代碼使用File.Exists(path)方法來(lái)判斷是否已經(jīng)存在同名文件,如果存在,就重新命名,直到名字唯一為止:

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++;
}

  一旦找到唯一的文件名后,立即將文件保存在文件系統(tǒng),同時(shí)更新ObjectDataSource控件的InsertParameter參數(shù)brochurePath的值,以便將文件名寫入數(shù)據(jù)庫(kù)。就像在教程《使用FileUpload上傳文件》里看到的一樣,可以使用FileUpload控件的SaveAs(path)方法來(lái)保存文件。使用e.Values集合來(lái)更新ObjectDataSource控件的參數(shù)brochurePath。

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

第7步:將上傳的圖片保存到數(shù)據(jù)庫(kù)

  為了把上傳的圖片保存在新添加的記錄里,我們需要在DetailsView控件的ItemInserting事件里,用上傳的數(shù)據(jù)對(duì)ObjectDataSource控件的picture參數(shù)賦值。然而,在此之前,我們需要確保上傳的文件為JPG而不是其它的什么格式。就象在第6步中探討的一樣,我們用文件的擴(kuò)展名來(lái)檢查其類型。

  雖然Categories表允許Picture列為NULL值,但所有的種類都應(yīng)該有一張圖片。在本頁(yè)面,我們強(qiáng)制用戶添加記錄時(shí)提供圖片。下面的代碼確保已經(jīng)上傳圖片,且為恰當(dāng)?shù)念愋汀?/p>

// 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;
}

這些代碼應(yīng)放在第6步中的代碼前面,如果上傳的文件有問(wèn)題,事件處理器在文件保存到文件系統(tǒng)前就結(jié)束了。

假設(shè)上傳的文件沒有問(wèn)題,然后我們用下面的代碼將上傳文件的數(shù)據(jù)分配給參數(shù)picture:

// 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頁(yè)面

  讓我們花幾分鐘測(cè)試我們?cè)谏蠋撞絼?chuàng)建的插入界面和ItemInserting事件處理器。在瀏覽器查看UploadInDetailsView.aspx頁(yè)面 ,嘗試添加一個(gè)類,忽略picture或指定一個(gè)非JPG的圖片或非PDF的小冊(cè)子。以上任何一種情況下,都會(huì)顯示一個(gè)錯(cuò)誤信息,并取消插入操作。


圖9:當(dāng)上傳的文件不對(duì)時(shí)將顯示一個(gè)警告信息

  確認(rèn)頁(yè)面要求上傳一張圖片,且不接受非PDF或非JPG文件。添加一個(gè)包含JPG格式圖片的新類別,將Brochure列置空,點(diǎn)擊Insert按鈕后,頁(yè)面回傳,將為Categories表添加一個(gè)新記錄,同時(shí)上傳的圖片數(shù)據(jù)直接存儲(chǔ)進(jìn)數(shù)據(jù)庫(kù)。GridView控件更新后,將新添加的類顯示出來(lái)。但是,就像圖10所示的那樣,類的圖片沒有正確的顯示出來(lái)。


圖10:新類的圖片沒有顯示出來(lái)

  圖片沒有顯示出來(lái)的原因是因?yàn)橛脕?lái)返回特定類的圖片的頁(yè)面DisplayCategoryPicture.aspx被設(shè)置為處理帶OLE報(bào)頭的位圖。當(dāng)Picture列的數(shù)據(jù)被返回到客戶端前已經(jīng)把那78字節(jié)的報(bào)頭剝離掉。而且上傳的JPG文件并沒有OLE報(bào)頭,因此,必需的字節(jié)已經(jīng)從圖片的二進(jìn)制數(shù)據(jù)移除了。

  由于現(xiàn)在表Categories里既有JPG文件又有帶OLE報(bào)頭的位圖,我們需要對(duì)頁(yè)面DisplayCategoryPicture.aspx做調(diào)整,使它對(duì)原來(lái)的8個(gè)類剝離OLE報(bào)頭,而不對(duì)新添加的類進(jìn)行剝離。在后面的教程,我們探討如何更新現(xiàn)有記錄的image文件,并將所有以前的類的圖片調(diào)整為JPG格式?,F(xiàn)在,我們?cè)陧?yè)面DisplayCategoryPicture.aspx 里用下面的代碼將原來(lái)的8個(gè)類的OLE報(bào)頭剝離。

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圖片現(xiàn)在可以正確的在GridView控件顯示出來(lái)了。


圖11:新添加的類的JPG圖片可以正確顯示了

第9步:出現(xiàn)異常時(shí)刪除Brochure文件

  將上傳文件保存在文件系統(tǒng)還面臨一個(gè)問(wèn)題,即無(wú)法將數(shù)據(jù)與存儲(chǔ)模式關(guān)聯(lián)起來(lái)。當(dāng)刪除一條記錄時(shí),存儲(chǔ)在文件系統(tǒng)的相應(yīng)文件也應(yīng)該刪除;類似地,添加記錄時(shí),亦然。假定這些情況:當(dāng)一個(gè)用戶添加一個(gè)新的種類時(shí),他指定了一張圖片和一份說(shuō)明小冊(cè)子。點(diǎn)擊Insert按鈕后,引發(fā)頁(yè)面回傳,發(fā)生DetailsView控件的ItemInserting事件,將文件保存到服務(wù)器文件系統(tǒng);接下來(lái),ObjectDataSource控件的Insert()方法調(diào)用CategoriesBLL類的InsertWithPicture方法,它又調(diào)用CategoriesTableAdapter的InsertWithPicture方法。

  如果數(shù)據(jù)庫(kù)剛好處于離線狀態(tài),或者INSERT SQL語(yǔ)句有錯(cuò)誤,那又會(huì)怎么樣呢?毫無(wú)疑問(wèn)添加記錄會(huì)失敗。最終結(jié)果是,向數(shù)據(jù)庫(kù)添加記錄失敗了,但卻成功地向服務(wù)器文件系統(tǒng)上傳了文件。當(dāng)插入過(guò)程拋出異常時(shí),應(yīng)該將該文件刪除。

  在教程18《在ASP.NET頁(yè)面中處理BLL/DAL層的異?!防?,我們提到體系構(gòu)架的不同層都可能拋出異常。在表現(xiàn)層,我們可以通過(guò)DetailsView控件的ItemInserted事件判斷是否發(fā)生了異常,同時(shí)提供ObjectDataSource控件的InsertParameters參數(shù)值。因此,我們?yōu)镮temInserted事件創(chuàng)建一個(gè)事件處理器,檢查是否拋出異常,如果是則刪除the ObjectDataSource控件的brochurePath參數(shù)指定的文件。

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()));
 }
}

總結(jié)

  我們要經(jīng)過(guò)幾個(gè)步驟來(lái)創(chuàng)建一個(gè)基于web的添加界面,該界面允許添加記錄包含二進(jìn)制數(shù)據(jù)。如果選擇直接存儲(chǔ)在數(shù)據(jù)庫(kù),我們將對(duì)體系結(jié)構(gòu)做一些調(diào)整,為了實(shí)現(xiàn)插入二進(jìn)制數(shù)據(jù),需要添加相應(yīng)的方法;調(diào)整完體系結(jié)構(gòu)下一步就需要?jiǎng)?chuàng)建一個(gè)添加界面,可以使用DetailsView控件,并定制其包含F(xiàn)ileUpload控件。上傳的文件可以存儲(chǔ)在服務(wù)器的文件系統(tǒng),或者在DetailsView控件的ItemInserting事件處理器里對(duì)一個(gè)數(shù)據(jù)源參數(shù)(data source parameter)賦值。

  將數(shù)據(jù)保存在文件系統(tǒng)還需要注意選用一個(gè)命名體系,以避免一個(gè)用戶上傳的文件將另一個(gè)用戶上傳的文件覆蓋掉。另外,當(dāng)向數(shù)據(jù)庫(kù)插入數(shù)據(jù)失敗時(shí),必須將上傳的文件刪除掉。

  現(xiàn)在我們可以向系統(tǒng)添加新的種類并附帶其圖片和說(shuō)明小冊(cè)子。在下一章我們探討如何更新現(xiàn)有的類,以及當(dāng)刪除一個(gè)類時(shí)如何正確的移除對(duì)應(yīng)的二進(jìn)制數(shù)據(jù)。

  祝編程快樂(lè)!

作者簡(jiǎn)介

  本系列教程作者 Scott Mitchell,著有六本ASP/ASP.NET方面的書,是4GuysFromRolla.com的創(chuàng)始人,自1998年以來(lái)一直應(yīng)用 微軟Web技術(shù)。大家可以點(diǎn)擊查看全部教程《[翻譯]Scott Mitchell 的ASP.NET 2.0數(shù)據(jù)教程》,希望對(duì)大家的學(xué)習(xí)ASP.NET有所幫助。

您可能感興趣的文章:
  • ASP.NET 站點(diǎn)地圖(sitemap)簡(jiǎn)明教程
  • 在ASP.NET 2.0中操作數(shù)據(jù)之五十一:從GridView的頁(yè)腳插入新記錄
  • 在ASP.NET 2.0中操作數(shù)據(jù)之五十二:使用FileUpload上傳文件
  • 在ASP.NET 2.0中操作數(shù)據(jù)之五十三:在Data Web控件顯示二進(jìn)制數(shù)據(jù)
  • 在ASP.NET 2.0中操作數(shù)據(jù)之五十五:編輯和刪除現(xiàn)有的二進(jìn)制數(shù)據(jù)
  • 在ASP.NET 2.0中操作數(shù)據(jù)之五十六:使用ObjectDataSource緩存數(shù)據(jù)
  • 在ASP.NET 2.0中操作數(shù)據(jù)之五十七:在分層架構(gòu)中緩存數(shù)據(jù)
  • 在ASP.NET 2.0中操作數(shù)據(jù)之五十八:在程序啟動(dòng)階段緩存數(shù)據(jù)
  • 在ASP.NET 2.0中操作數(shù)據(jù)之五十九:使用SQL緩存依賴項(xiàng)SqlCacheDependency
  • 在ASP.NET 2.0中操作數(shù)據(jù)之六十:創(chuàng)建一個(gè)自定義的Database-Driven Site Map Provider

標(biāo)簽:慶陽(yáng) 甘肅 中衛(wèi) 聊城 臨夏 清遠(yuǎn) 海西

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《在ASP.NET 2.0中操作數(shù)據(jù)之五十四:添加新記錄時(shí)包含一個(gè)文件上傳選項(xiàng)》,本文關(guān)鍵詞  在,ASP.NET,2.0,中,操作,數(shù)據(jù),;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《在ASP.NET 2.0中操作數(shù)據(jù)之五十四:添加新記錄時(shí)包含一個(gè)文件上傳選項(xiàng)》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于在ASP.NET 2.0中操作數(shù)據(jù)之五十四:添加新記錄時(shí)包含一個(gè)文件上傳選項(xiàng)的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    临猗县| 邹平县| 封开县| 屏边| 佛冈县| 乳山市| 离岛区| 喀喇| 九龙坡区| 夹江县| 临沭县| 高陵县| 石城县| 吴旗县| 长乐市| 水富县| 沧州市| 黄石市| 永川市| 沙坪坝区| 乌海市| 辽宁省| 杭锦后旗| 仙桃市| 东乌| 巴里| 武冈市| 清丰县| 新沂市| 商水县| 建宁县| 康保县| 阳朔县| 广宁县| 淮南市| 延吉市| 留坝县| 安溪县| 平江县| 三穗县| 镇原县|