導(dǎo)言:
到目前為止,我們的教程圍繞的是text數(shù)據(jù)。然而,很多應(yīng)用程序既需要處理text數(shù)據(jù),也需要處理二進(jìn)制數(shù)據(jù)。比如招聘網(wǎng)站可能需要用戶上傳Word或PDF格式的簡(jiǎn)歷。
使用二進(jìn)制數(shù)據(jù)面臨一項(xiàng)挑戰(zhàn):在應(yīng)用程序中如何存儲(chǔ)二進(jìn)制數(shù)據(jù)。我們必須更新添加記錄的界面以支持用戶上傳本地電腦中的文件,并添加額外的功能以下載某條記錄的相關(guān)二進(jìn)制數(shù)據(jù)。本章以及接下來(lái)的3章,我們探討如何處理這些問(wèn)題。在本系列教程結(jié)束時(shí),我們將創(chuàng)建一個(gè)功能完善的應(yīng)用程序,它為每種類型的記錄提供相關(guān)的圖片和PDF小冊(cè)子。 在本系列教程,我們探討存儲(chǔ)二進(jìn)制數(shù)據(jù)的各種方法,考察如何允許用戶從自己的電腦上傳文件并存儲(chǔ)在服務(wù)器的文件系統(tǒng)里。
注意:二進(jìn)制數(shù)據(jù)有時(shí)候被稱為“BLOB”(Binary Large OBject的縮寫)。本教程我選擇使用術(shù)語(yǔ)“binary data”,即使它和術(shù)語(yǔ)BLOB同意。
第1步: 添加Working with Binary Data教程頁(yè)
我們先花一點(diǎn)時(shí)間在網(wǎng)站里創(chuàng)建一些頁(yè),這些頁(yè)會(huì)在本教程里用到.先添加一個(gè)名為BinaryData的文件夾,然后添加如下頁(yè)面.確保每頁(yè)都選擇了Site.master作為母板頁(yè).
Default.aspx
FileUpload.aspx
DisplayOrDownloadData.aspx
UploadInDetailsView.aspx
UpdatingAndDeleting.aspx
![](/d/20211017/9838574ce88ba2d0ddc0c15dc96d454c.gif)
圖1:添加所需要的頁(yè)面
象其它文件夾一樣,BinaryData文件夾里的Default.aspx 用來(lái)列出教程章節(jié).記得SectionLevelTutorialListing.ascx 這個(gè)用戶控件提供了這個(gè)功能.因此,從解決方案瀏覽里將這個(gè)用戶控件拖到頁(yè)面上.
![](/d/20211017/5aefcbf5c96148521229aee619a44d76.gif)
圖2:添加SectionLevelTutorialListing.ascx 用戶控件 到Default.aspx
最后,將這些頁(yè)的地址加到 Web.sitemap 的條目里.在Enhancing the GridView siteMapNode>之后添加下面的標(biāo)記.
siteMapNode
title="Working with Binary Data"
url="~/BinaryData/Default.aspx"
description="Extend the data model to include collecting binary data.">
siteMapNode
title="Uploading Files"
url="~/BinaryData/FileUpload.aspx"
description="Examine the different ways to store binary data on the
web server and see how to accept uploaded files from users
with the FileUpload control." />
siteMapNode
title="Display or Download Binary Data"
url="~/BinaryData/DisplayOrDownloadData.aspx"
description="Let users view or download the captured binary data." />
siteMapNode
title="Adding New Binary Data"
url="~/BinaryData/UploadInDetailsView.aspx"
description="Learn how to augment the inserting interface to
include a FileUpload control." />
siteMapNode
title="Updating and Deleting Existing Binary Data"
url="~/BinaryData/UpdatingAndDeleting.aspx"
description="Learn how to update and delete existing binary data." />
/siteMapNode>
修改完Web.sitemap后,在瀏覽器里看一下本教程站點(diǎn)。
![](/d/20211017/c83a38262cd9ee6523989e49cb957f0a.gif)
圖3:Site Map包含了本教程
第2步:將二進(jìn)制數(shù)據(jù)存儲(chǔ)在什么地方
有2種方式存放二進(jìn)制數(shù)據(jù):一種是將其存儲(chǔ)在服務(wù)器的文件系統(tǒng)里,并將文件路徑存儲(chǔ)在數(shù)據(jù)庫(kù)里;第二種是直接將其存儲(chǔ)在數(shù)據(jù)庫(kù)里(見(jiàn)圖4)。2種方法各有其優(yōu)點(diǎn)和缺點(diǎn)。
![](/d/20211017/9e9ba1b31a8bdeef051a7ecf7cad7cd8.gif)
圖4:可以將二進(jìn)制數(shù)據(jù)存儲(chǔ)在文件系統(tǒng)或直接放在數(shù)據(jù)庫(kù)
假設(shè)我們對(duì)數(shù)據(jù)庫(kù)Northwind進(jìn)行擴(kuò)展,每個(gè)產(chǎn)品對(duì)應(yīng)一幅圖片。一種方法是在服務(wù)器文件系統(tǒng)存儲(chǔ)這些圖片,然后在表Products里記錄該圖片的文件路徑。為此,我們要在
Products表里添加一列,名為ImagePath,類型是varchar(200)。假設(shè)用戶為產(chǎn)品Chai上傳一張圖片時(shí),圖片可能存放在服務(wù)器文件系統(tǒng)的~/Images/Tea.jpg位置。這里,~代表應(yīng)用程序的物理位置。也就是說(shuō),如果該網(wǎng)站根植于C:/Websites/Northwind/的話,~/Images/Tea.jpg相當(dāng)于C:/Websites/Northwind/Images/Tea.jpg 。上傳圖片后,我們應(yīng)在表Products更新記錄Chai,使其ImagePath列引用圖片的路徑。如果我們決定將所有產(chǎn)品的圖片放在應(yīng)用程序的Images文件夾,我們可以使用“~/Images/Tea.jpg”或“Tea.jpg”來(lái)表示。
將二進(jìn)制數(shù)據(jù)放置在文件系統(tǒng)的主要優(yōu)點(diǎn)在于:
1. 執(zhí)行方便——就像我們即將看到的那樣,將二進(jìn)制數(shù)據(jù)直接放置在數(shù)據(jù)庫(kù)和存儲(chǔ)于文件系統(tǒng)相比,當(dāng)用戶需要存儲(chǔ)并獲取數(shù)據(jù)時(shí)需要更多的代碼。另外,為使用戶查看或下載數(shù)據(jù),必須用到定位于該數(shù)據(jù)的URL(譯注:統(tǒng)一資源定位器)。如果數(shù)據(jù)存儲(chǔ)在文件系統(tǒng),URL是直觀明了的;如果是存儲(chǔ)在數(shù)據(jù)庫(kù)里,則必須創(chuàng)建一個(gè)頁(yè)面來(lái)獲取并返回?cái)?shù)據(jù)。
2.訪問(wèn)范圍寬——其它的服務(wù)或程序有時(shí)需要訪問(wèn)二進(jìn)制數(shù)據(jù),但當(dāng)二進(jìn)制數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫(kù)中時(shí),這些服務(wù)或程序便無(wú)法訪問(wèn)了。比如,用戶可能希望通過(guò)FTP來(lái)訪問(wèn)每個(gè)產(chǎn)品對(duì)應(yīng)的圖片,在這種情況下,最好將其放置在文件系統(tǒng)。
3.更好的執(zhí)行效能——將二進(jìn)制數(shù)據(jù)放置在文件系統(tǒng)和數(shù)據(jù)庫(kù)相比,在數(shù)據(jù)庫(kù)服務(wù)和服務(wù)器服務(wù)之間的查詢和網(wǎng)絡(luò)堵塞情況要少一些。
將二進(jìn)制數(shù)據(jù)放置在文件系統(tǒng)的主要缺點(diǎn)在于削弱了數(shù)據(jù)的關(guān)聯(lián)性。比如我們從表Products刪除一條記錄時(shí),放在文件系統(tǒng)中的相關(guān)文件不會(huì)自動(dòng)刪除,因此我們必須手寫代碼將其刪除。不然的話,隨著文件碎片的慢慢積累,文件系統(tǒng)會(huì)變的混亂不堪。另外,對(duì)數(shù)據(jù)庫(kù)的任何改動(dòng),都要對(duì)在文件系統(tǒng)里的相應(yīng)二進(jìn)制數(shù)據(jù)做修改。比如將數(shù)據(jù)庫(kù)轉(zhuǎn)移到另外的站點(diǎn)或服務(wù)器時(shí)便面臨這種挑戰(zhàn)。
做為選擇,你可以在Microsoft SQL Server 2005里創(chuàng)建一個(gè)類型為varbinary的列,用于存儲(chǔ)二進(jìn)制數(shù)據(jù)。你可以指定存儲(chǔ)數(shù)據(jù)的最大長(zhǎng)度,比如你希望數(shù)據(jù)的最大長(zhǎng)度不超過(guò)5000字節(jié),指定類型為varbinary(5000); 而varbinary(MAX)是Microsoft SQL Server 2005能提供的最大存儲(chǔ)空間,大概 2 GB.
將二進(jìn)制數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫(kù)的主要優(yōu)點(diǎn)是:將數(shù)據(jù)庫(kù)記錄和二進(jìn)制數(shù)據(jù)關(guān)聯(lián)起來(lái)。它極大的簡(jiǎn)化了數(shù)據(jù)庫(kù)的管理,比如將數(shù)據(jù)庫(kù)轉(zhuǎn)移到另一個(gè)網(wǎng)站或服務(wù)器。同樣,當(dāng)刪除一條記錄時(shí),同時(shí)自動(dòng)的刪除了相關(guān)的二進(jìn)制數(shù)據(jù)。
注意:在Microsoft SQL Server 2000及更早本版,varbinary類型最大支持8000字節(jié),要支持2GB的二進(jìn)制數(shù)據(jù),就要使用image類型了。在SQL Server 2005里引入MAX后,image類型已經(jīng)開(kāi)始被淡化了。盡管向后兼容,微軟宣稱將在SQL Server的后續(xù)版本中拋棄image類型。
如果你使用的是較早的數(shù)據(jù)類型,你可能看見(jiàn)過(guò)image類型。在數(shù)據(jù)庫(kù)Northwind里的表Categories有一個(gè)Picture列,可用來(lái)存儲(chǔ)某個(gè)類的二進(jìn)制圖像文件。由于數(shù)據(jù)庫(kù)Northwind起源于Microsoft Access以及SQL Server早期版本,所以Picture列的類型為image。
本章及接下來(lái)的3章,我們2種方法都要用。表Categories已經(jīng)有列Picture來(lái)存儲(chǔ)類的二進(jìn)制圖片文件,我們還要一個(gè)額外的列BrochurePath來(lái)存儲(chǔ)文件系統(tǒng)里PDF的路徑。
第3步:為表Categories添加BrochurePath列
目前,表Categories只包含了4個(gè)列:CategoryID, CategoryName, Description以及 Picture。除此以外,我們還需要添加一列,指向該類的小冊(cè)子(如果存在的話)。打開(kāi)服務(wù)器資源管理器,點(diǎn)擊表節(jié)點(diǎn),右鍵點(diǎn)擊表Categories,選擇“打開(kāi)表定義”(見(jiàn)圖5)。如果看不到服務(wù)器資源管理器,在視圖菜單里選擇它,或按Ctrl+Alt+S.
在表Categories里添加一個(gè)名為BrochurePath的列,類型為varchar(200),允許其值為NULL。點(diǎn)擊保存按鈕(或按Ctrl+S)。
![](/d/20211017/54259e057352a719a74c87e120568dba.gif)
圖5:表Categories里添加BrochurePath的列
第4步:更新體系構(gòu)架以使用Picture 和 BrochurePath列
當(dāng)前,數(shù)據(jù)訪問(wèn)層(Data Access Layer)里的CategoriesDataTable定義了4個(gè)DataColumns:CategoryID, CategoryName, Description以及NumberOfProducts.我們最初在教程《創(chuàng)建一個(gè)數(shù)據(jù)訪問(wèn)層》里創(chuàng)建CategoriesDataTable時(shí),其只包含了前3個(gè)列,而NumberOfProducts列是在第35章《使用Repeater和DataList單頁(yè)面實(shí)現(xiàn)主/從報(bào)表》里創(chuàng)建的。
就像在教材《創(chuàng)建一個(gè)數(shù)據(jù)訪問(wèn)層》里討論的那樣,DataTables以類型DataSet的形式構(gòu)成了業(yè)務(wù)對(duì)象。TableAdapters的作用在于連接數(shù)據(jù)庫(kù),并以查詢結(jié)果組成
業(yè)務(wù)對(duì)象。CategoriesDataTable是由CategoriesTableAdapter構(gòu)成的,它包含三種數(shù)據(jù)訪問(wèn)方法:
GetCategories():執(zhí)行該TableAdapter的主要查詢,返回記錄的CategoryID, CategoryName和Description這3項(xiàng)。自動(dòng)生成的Insert和Update方法主要就是用的該方法。
GetCategoryByCategoryID(categoryID):也是返回記錄的CategoryID, CategoryName和Description這3項(xiàng),前提是CategoryID值要匹配。
GetCategoriesAndNumberOfProducts():也是返回記錄的CategoryID, CategoryName和Description這3項(xiàng),同時(shí)返回每種類別產(chǎn)品的數(shù)量。
我們注意到,這些方法沒(méi)有返回表Categories的Picture或BrochurePath;CategoriesDataTable里也沒(méi)有展示這些列。為了能夠使用到Picture和BrochurePath,我們需要先在CategoriesDataTable里添加它們,再更新類CategoriesTableAdapter,使其返回它們的值。
添加Picture和BrochurePath列
首先將它們添加到CategoriesDataTable.在CategoriesDataTable的頂部點(diǎn)右鍵,從菜單里選“添加”,再選“列”選項(xiàng)。這將為DataTable 創(chuàng)建一個(gè)名為Column1的數(shù)據(jù)列(DataColumn),將其重命名為Picture。在屬性窗口里將其DataType屬性設(shè)置為“System.Byte[]”(在下拉列表里沒(méi)該選擇,故需手動(dòng)輸入)
![](/d/20211017/921d3b4eec13b03b5cdf75c561cbdbba.gif)
圖6:創(chuàng)建一個(gè)名為Picture的數(shù)據(jù)列,設(shè)數(shù)據(jù)類型為System.Byte[]
添加另一個(gè)名為BrochurePath的數(shù)據(jù)列,使用默認(rèn)的數(shù)據(jù)類型(System.String)
從TableAdapter返回值Picture和BrochurePath
在CategoriesDataTable里添加了上述2列后,我們準(zhǔn)備更新CategoriesTableAdapter。我們可以在TableAdapter的主查詢里返回這2個(gè)值,但每次調(diào)用GetCategories()都會(huì)返回二進(jìn)制數(shù)據(jù)。怎么才好呢?我們可以讓主查詢返回BrochurePath值,而另外創(chuàng)建一個(gè)方法來(lái)返回某個(gè)特定種類的Picture值。
為了更新主查詢,在CategoriesTableAdapter的頂部右鍵點(diǎn)擊,選“配置”項(xiàng),這將啟動(dòng)Table Adapter設(shè)置向?qū)В虏樵兪蛊浞祷谺rochurePath,點(diǎn)完成。
![](/d/20211017/02b742862d7a7f34cf7aa0bd9a9e9da8.gif)
圖7:更新SELECT命令,返回BrochurePath
使用ad-hoc SQL語(yǔ)句更新TableAdapter里的主查詢時(shí),它將更新TableAdapter里所有方法的查詢涉及的列。也即,更新GetCategoryByCategoryID(categoryID) 方法,使其返回BrochurePath值;更新GetCategoriesAndNumberOfProducts()方法,并刪除返回每種類別有幾個(gè)產(chǎn)品的查詢(也即刪除NumberOfProducts)因此,我們右鍵點(diǎn)擊GetCategoriesAndNumberOfProducts(),選擇“配置”,使其恢復(fù)為原來(lái)的查詢語(yǔ)句:
SELECT CategoryID, CategoryName, Description,
(SELECT COUNT(*)
FROM Products p
WHERE p.CategoryID = c.CategoryID)
as NumberOfProducts
FROM Categories c
然后,創(chuàng)建一個(gè)返回某個(gè)特定種類Picture值的TableAdapter方法。在CategoriesTableAdapter的頂部點(diǎn)擊右鍵,選擇“添加查詢”,這將打開(kāi)TableAdapter的查詢?cè)O(shè)置向?qū)АJ紫冗x擇如何訪問(wèn)數(shù)據(jù)庫(kù),有3種方式:使用SQL語(yǔ)句、創(chuàng)建一個(gè)新的存儲(chǔ)過(guò)程、使用一個(gè)已有的存儲(chǔ)過(guò)程。我們選擇“使用SQL語(yǔ)句”,點(diǎn)下一步,在界面里選“SELECT(返回行)(S)”。
![](/d/20211017/7f3a558feee69d486b3ca3792b5b34a9.gif)
圖8:選擇使用SQL語(yǔ)句
![](/d/20211017/46633f719fa27a72764a318abea329fd.gif)
圖9:由于查詢要返回一條記錄,我們選“選擇行”。
點(diǎn)下一步,鍵入如下SQL查詢,點(diǎn)Next:
SELECT CategoryID, CategoryName, Description, BrochurePath, Picture
FROM Categories
WHERE CategoryID = @CategoryID
最后,為新方法命名。為填充DataTable的方法命名為FillCategoryWithBinaryDataByCategoryID;為返回DataTable的方法命名為GetCategoryWithBinaryDataByCategoryID。點(diǎn)Finish完成設(shè)置。
![](/d/20211017/e52eab9536676abaee96506fe49013dc.gif)
圖10:為TableAdapter的方法命名
注意:完成設(shè)置后,我們將看到一個(gè)對(duì)話框,提示你“新命令文本所返回?cái)?shù)據(jù)的架構(gòu)與主查詢的構(gòu)架不同。如果不期望出現(xiàn)此情況,請(qǐng)檢查查詢的命令文本?!?。簡(jiǎn)而言之,向?qū)б庾R(shí)到主查詢—GetCategories() ,和我們剛剛創(chuàng)建的方法返回的數(shù)據(jù)不同。這正是我們希望的結(jié)果,不用理會(huì)它。
同時(shí),謹(jǐn)記,如果你在向?qū)Ю镞x用ad-hoc SQL語(yǔ)句,然后再改變TableAdapter的主查詢,它將改變GetCategoryWithBinaryDataByCategoryID 方法里SELECT命令所涉及到的列,以包含主查詢使用的那些列(也就是從查詢里移除Picture列)。我們必須手動(dòng)更新代碼,使其返回Picture ,和我們?cè)谇懊嫣幚鞧etCategoriesAndNumberOfProducts() 方法類似。
當(dāng)完成在CategoriesDataTable添加2個(gè)數(shù)據(jù)列,以及對(duì)CategoriesTableAdapter添加GetCategoryWithBinaryDataByCategoryID方法后,這些類在數(shù)據(jù)集設(shè)計(jì)器里看起來(lái)應(yīng)和圖11差不多。
![](/d/20211017/ba9976389cf7074793ca4a06c3cb4926.gif)
圖11:DataSet Designer包含了新列和新方法
更新業(yè)務(wù)邏輯層(BLL)
更新數(shù)據(jù)訪問(wèn)層后,接下來(lái)就要擴(kuò)展業(yè)務(wù)邏輯層以應(yīng)對(duì)新添加的CategoriesTableAdapter方法,在類CategoriesTableAdapter添加如下代碼:
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, false)]
public Northwind.CategoriesDataTable
GetCategoryWithBinaryDataByCategoryID(int categoryID)
{
return Adapter.GetCategoryWithBinaryDataByCategoryID(categoryID);
}
第5步:從客戶端上傳一個(gè)文件到服務(wù)器
通常情況下,用戶將本地計(jì)算機(jī)上的文件上傳到服務(wù)器,這就涉及到以何種形式來(lái)處理數(shù)據(jù),是直接將它存儲(chǔ)在數(shù)據(jù)庫(kù)?還是將它存儲(chǔ)在文件系統(tǒng),然后在數(shù)據(jù)庫(kù)存儲(chǔ)其文件路徑?在這里,我們探討如何將文件上傳到服務(wù)器,在后面的教程我們專注于上傳文件的數(shù)據(jù)模式。
ASP.NET 2.0新增加的FileUpload Web控件提供了一種機(jī)制,供用戶上傳文件。它是一種type屬性為“file”的input>元件。在瀏覽器里看起來(lái)就像是一個(gè)帶Browse按鈕的文本框。當(dāng)點(diǎn)擊Browse按鈕后,便彈出一個(gè)對(duì)話框供用戶選擇文件。選定,回傳后,被選文件的內(nèi)容一起被傳遞會(huì)服務(wù)器。在服務(wù)器端,我們可以通過(guò)FileUpload控件的屬性獲取上傳文件的信息。
為驗(yàn)證上傳文件,打開(kāi)BinaryData文件夾的FileUpload.aspx頁(yè)面并切換到設(shè)計(jì)模式。從工具箱拖一個(gè)FileUpload控件到頁(yè)面,設(shè)其ID為UploadTest。再添加一個(gè)Button控件,設(shè)其ID為UploadButton,Text屬性為“Upload Selected File”。最后,在Button控件下添加一個(gè)Label控件,設(shè)其ID為UploadDetails并清空Text屬性。
![](/d/20211017/2399b879c7d3c86a8755d1d727f7361f.gif)
圖12:在ASP.NET頁(yè)面上添加一個(gè)FileUpload控件
圖13為在瀏覽器查看該頁(yè)面的情形,我們注意到,當(dāng)點(diǎn)擊Browse按鈕時(shí),彈出一個(gè)對(duì)話框,供用戶選擇要上傳的文件。當(dāng)選定一個(gè)文件,再點(diǎn)“Upload Selected File”按鈕時(shí),頁(yè)面回傳,將文件的二進(jìn)制數(shù)據(jù)傳到服務(wù)器。
![](/d/20211017/e6e177100cf90c057d601fa25de68a45.gif)
圖13:用戶選擇上傳到服務(wù)器的文件
頁(yè)面回傳后,上傳的文件可以存儲(chǔ)于文件系統(tǒng),也能以“流”(Stream)的形式被調(diào)用。本例,我們將其存儲(chǔ)在文件夾~/Brochures 里。首先在根目錄創(chuàng)建一個(gè)Brochures文件夾,然后為UploadButton的Click事件創(chuàng)建一個(gè)事件處理器:
protected void UploadButton_Click(object sender, EventArgs e)
{
if (UploadTest.HasFile == false)
{
// No file uploaded!
UploadDetails.Text = "Please first select a file to upload...";
}
else
{
// Display the uploaded file's details
UploadDetails.Text = string.Format(
@"Uploaded file: {0}br />
File size (in bytes): {1:N0}br />
Content-type: {2}",
UploadTest.FileName,
UploadTest.FileBytes.Length,
UploadTest.PostedFile.ContentType);
// Save the file
string filePath =
Server.MapPath("~/Brochures/" + UploadTest.FileName);
UploadTest.SaveAs(filePath);
}
}
FileUpload控件提供了多種處理上傳數(shù)據(jù)的屬性。比如,HasFile屬性指出用戶是否上傳了文件;FileBytes屬性以字節(jié)的形式訪問(wèn)數(shù)據(jù)。Click事件處理器事先確定是否上傳文件,如果是,Label控件列出文件的名稱、字節(jié)大小和類型。
注意:為確保用戶上傳文件,可以使用RequiredFieldValidator控件或檢查HasFile屬性,若其為false,則提出警告信息。
FileUpload控件的SaveAs(filePath)屬性將文件存儲(chǔ)在一個(gè)指定的文件路徑(filePath)。路徑必須是一個(gè)物理路徑(如C:/Websites/Brochures/SomeFile.pdf)),而不是相對(duì)路徑(如:/Brochures/SomeFile.pdf). 方法Server.MapPath(virtPath)截取的是相對(duì)路徑,返回的是物理路徑。這里,相對(duì)路徑是~/Brochures/fileName, 其fileName就是上傳文件的名字。
完成Click事件處理器后,花幾分鐘在瀏覽器查看該頁(yè),點(diǎn)Browse按鈕,從硬盤選擇一個(gè)文件供上傳。再點(diǎn)“Upload Selected File”按鈕,頁(yè)面回傳,將文件上傳到 ~/Brochures文件夾里。上傳文件后,返回Visual Studio,在解決資源管理器里點(diǎn)“刷新”按鈕,你將會(huì)在~/Brochures文件夾里看到剛剛上傳的文件。
![](/d/20211017/42ff6bd17c113bc3ace43eea1a97dddb.gif)
圖14:文件已經(jīng)上傳到服務(wù)器
![](/d/20211017/2bc588788e264a81603015e9d8b159f0.gif)
圖15:上傳文件保存在~/Brochures文件夾
將上傳文件保存在文件系統(tǒng)
當(dāng)將上傳文件保存到文件系統(tǒng)時(shí),有幾個(gè)要點(diǎn)需要注意。首先是安全問(wèn)題,要將文件保存到文件系統(tǒng),運(yùn)行的頁(yè)面必須要有寫入權(quán)限。如果你使用Microsoft's Internet Information Services (IIS) 當(dāng)服務(wù)器,其安全性取決于IIS的版本和設(shè)置。
另一個(gè)挑戰(zhàn)是對(duì)上傳文件的命名問(wèn)題。一般來(lái)說(shuō),頁(yè)面將文件保存在~/Brochures 文件夾時(shí),直接使用其在用戶電腦時(shí)的原名。比如,用戶A上傳一個(gè)名為Brochure.pdf的文件,上傳后保存為~/Brochure/Brochure.pdf,假如稍后用戶B也要上傳文件,且名稱也剛好為Brochure.pdf,會(huì)發(fā)生什么情況呢?就我們目前的代碼而言,用戶B的文件將覆蓋用戶A的文件。
有幾種處理命名沖突的方法。一種是當(dāng)存儲(chǔ)同名文件時(shí),阻止上傳文件。比如當(dāng)用戶B試圖上傳一個(gè)名為Brochure.pdf的文件時(shí),提示其改名并重試。另一個(gè)方法是使用一種唯一標(biāo)識(shí)的文件名,可以是globally unique identifier (GUID),或?qū)?yīng)數(shù)據(jù)庫(kù)記錄的主鍵值。在下一節(jié)教程我們將詳細(xì)探討。
處理大容量二進(jìn)制數(shù)據(jù)的面臨的問(wèn)題
教程假設(shè)處理的二進(jìn)制數(shù)據(jù)是適度大小的。處理大數(shù)據(jù)量的二進(jìn)制數(shù)據(jù)時(shí)—比如幾MB,甚至更大,就超出本教程的范圍了。默認(rèn)情況下, ASP.NET 最多處理4MB的數(shù)據(jù),雖然可以在Web.config文件的httpRuntime>元素里設(shè)置能處理的最大值,但I(xiàn)IS同樣對(duì)上傳文件的大小做了限制,見(jiàn)文章《IIS Upload File Size》。
另外,上傳大數(shù)據(jù)量文件花費(fèi)的時(shí)間很可能超過(guò)ASP.NET默認(rèn)等待的110秒,還要面對(duì)處理大數(shù)據(jù)的內(nèi)存、執(zhí)行效率問(wèn)題。
FileUpload控件不太適合上傳大的文件。當(dāng)上傳大文件時(shí),用戶需要耐心的等待,且無(wú)法確定上傳是否在進(jìn)行。上傳小文件時(shí)問(wèn)題不大,也許只要幾秒鐘;而大文件可能要幾分鐘。有很多第三方的文件上傳控件更適合上傳大文件且提供進(jìn)度提示,提供更好的用戶體驗(yàn)。
如果你的應(yīng)用程序需要處理大文件,你要認(rèn)真考慮面臨的挑戰(zhàn),尋找適合自己的解決方案。
總結(jié):
創(chuàng)建一個(gè)處理二進(jìn)制數(shù)據(jù)的應(yīng)用程序面臨一些問(wèn)題。本教程探討了其中的2個(gè)問(wèn)題:如何存儲(chǔ)數(shù)據(jù)以及允許用戶通過(guò)頁(yè)面上傳文件。接下來(lái)的教程我們探討如何處理數(shù)據(jù)庫(kù)記錄中的二進(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ù)之五十三:在Data Web控件顯示二進(jìn)制數(shù)據(jù)
- 在ASP.NET 2.0中操作數(shù)據(jù)之五十四:添加新記錄時(shí)包含一個(gè)文件上傳選項(xiàng)
- 在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