一、 引言
在建構資料驅動的應用程式時,經常需要擷取文字和二進位資料。這樣的程式可能需要儲存影像,PDF,Word檔案或其它二進位資料。能夠使用兩種方式來儲存這些二進位資料:儲存在web伺服器的檔案系統上並添加一個對資料庫中對應文件的引用;或直接儲存在資料庫本身。
文字數據,例如字串,數字,日期,GUID,貨幣值,等等-在資料庫系統中都有適當的和相應的資料類型定義。例如,在Microsoft SQL Server中,你可以使用int資料類型來儲存一個整數值;而為了儲存一個字串值,你可以使用一個varchar或nvarchar類型。另外,資料庫也提供了用於儲存二進位資料的類型定義。在Microsoft SQL SERVER 2000及早期版本中,使用image資料型別來儲存二進位資料;而在SQL SERVER 2005中,使用varbinary(MAX)資料型別。使用上面兩種方式中的任何一種,這些資料類型都能夠儲存可達2GB大小的二進位資料。
不過,當直接把二進位資料儲存在資料庫時,需要增加一些額外工作來實現插入、更新和檢索二進位資料。幸好,我們可以透過更高階的資料存取函式庫-例如ADO.NET-對這種複雜的低階T-SQL操作加以抽象,從而使問題變得相當簡單。然而,透過ADO.NET方式使用二進位資料與使用文字資料的確有點不同。在本文中,我們將分析如何使用ADO.NET和ASP.NET 2.0 SqlDataSource控制項直接透過一個資料庫來儲存和擷取影像檔案。請接著往下閱讀!
二、 把資料儲存在資料庫中與儲存在檔案系統中的比較
正如剛才介紹的,當擷取一個應用程式中的二進位資料時,該二進位資料既可以直接儲存在資料庫中也可以作為一個檔案保存在web伺服器的檔案系統中-僅保持一個對資料庫中檔案的引用。根據我的體驗,我發現大多數開發者更喜歡在文件系統中存儲二進制數據,這主要基於下列原因:
· 需要較少的工作-存儲和檢索存儲在數據庫中的二進制需要更多的編碼工作。而且,更新這些二進位資料也會更容易-不需要與資料庫通訊,只須直接修改文件即可!
· 指向檔案的URL更為直接-正如我們將在本文中所看到的,為了提供訪問儲存在一個資料庫中的二進位數據,我們需要建立另一個能夠返回該資料的ASP.NET頁面。典型地,會把對應於資料庫中對應記錄(傳回它的二進位資料)的一個唯一的識別碼傳遞給這個頁面。結果是,為了訪問該二進位資料-比方說一個上傳的映像-該URL看上去如http://www.yourserver.com/ShowImage.aspx?ID=4352的形式,而如果該映像直接儲存在文件系統中,URL將更為直接些-例如http: //www.yourserver.com/UploadedImages/Sam.jpg。
· 為顯示影像提供更好的工具支援-如果你在使用ASP.NET 2.0,那麼,你可以在GridView或DetailsView控制項中使用ImageField控制項來顯示一個影像(它的影像路徑儲存在資料庫中)。然而,遺憾的是,這個ImageField卻無法直接顯示資料庫中的影像資料(既然它要求查詢一個外部頁面並且傳回對應的資料)。
· 效能-既然二進位檔案儲存在網頁伺服器的檔案系統而不是儲存在資料庫中,那麼,應用程式可以存取資料庫中較少的數據,從而減少了對資料庫的要求,也相應地減少了存在於web和資料庫伺服器之間的網路擁擠。
把資料直接儲存在資料庫的主要優點在於,它能夠使資料成為"自包含的"。既然所有的資料都包含在資料庫中,那麼,資料支援、資料在資料庫伺服器間的移動以及資料庫複製等等就容易得多了,因為不存在擔心複製或備份儲存在檔案系統中的二進位內容這樣的問題。
如往常一樣,至於選擇哪種儲存方案要具體依賴實際的使用場所和業務需求。例如,我開發過一個客戶端,其中的二進位資料必須儲存在資料庫中,因為它們使用的報告軟體僅能夠在報告中包括二進位資料-如果它來自於資料庫的話。在另一種情況下,我的一個同事正在開發一個工程,其中的二進位檔案需要為web應用程式使用並且可經由FTP使用,這種情況很有必要把二進位資料儲存在檔案系統中。
三、 創建一個存儲二進制資料的資料庫表格
本文中的其它部分將分析一個簡單的ASP.NET2.0圖像畫廊應用程序,我使用微軟SQL Server 2005 Express Edition編寫的,用於展示本文所闡述的直接從一個資料庫中儲存和檢索二進位資料的相關概念。
這個圖像畫廊應用程式的資料模型包括一個表格-Pictures,其中的每一個記錄對應畫廊中的一張圖片。這個Pictures表格的MIMEType 域中儲存了上載圖像(對於JPG檔案是image/jpeg,對於GIF檔案是image/gif,等等)的MIME類型;這裡的MIME類型向瀏覽器指定如何產生該二進位資料。其中的ImageData列則儲存了該圖片實際的二進位內容。
四、 上傳一個圖像並使用ADO.NET程式碼儲存二進位資料
這個圖像畫廊允許訪客上傳圖片檔案(GIF, JPG和PNG格式)到這個應用程式。一旦上傳,新的記錄將會被加入到Pictures表格並且該影像檔案的內容即儲存在新的記錄的ImageData列內。為了實作在ASP.NET 2.0中把檔案從web瀏覽器端上傳到web伺服器,本範例中使用了FileUpload控制項。 FileUpload控制項的使用方法是很簡單的事情-只需要把它從工具列拖曳到你的頁面上即可。最終,這個FileUpload控制項將在使用者的瀏覽器端產生為標準的檔案上傳形式-一個"Browse"按鈕(當點擊它時)允許使用者從他們的硬碟中選擇一個檔案上傳到web伺服器。
例如,為了建立一個介面以實現新增一個新的映像,我使用一個TextBox控制項來擷取圖片的標題,還有一個FileUpload控制項用於允許使用者指定要上傳的映像:
<b>Title:</b>
<asp:TextBox ID="PictureTitle" runat="server" />
<br />
<b>Picture:</b>
<asp:FileUpload ID="UploadedFile" runat="server" />
<br />
<asp:LinkButton ID="btnInsert" runat="server" Text="Insert" />
<asp:LinkButton ID="btnCancel" runat="server" Text="Cancel" />
上面的程式碼建立一個頁面,使用者能夠從他們的硬碟上指定一個要上傳到web伺服器的檔案。
一旦使用者選擇了一個檔案並且寄送了這個表單(例如透過點擊"Insert"按鈕),那麼,指定檔案的二進位內容即被寄送到web伺服器。從伺服器端程式碼中,這種二進位資料透過FileUpload控制項的PostedFile.InputStream屬性成為可用,這正如下面的標記和程式碼所顯示的:
Protected Sub btnInsert_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnInsert .Click
'確保一個檔案被成功上傳
If UploadedFile.PostedFile Is Nothing OrElse String.IsNullOrEmpty(UploadedFile.PostedFile.FileName) OrElse UploadedFile.PostedFile.InputStream Is Nothing Then
... 顯示錯誤訊息...
Exit Sub
End If
'確保我們在操作一個JPG或GIF文件
Dim extension As String = Path.GetExtension(UploadedFile.PostedFile.FileName).ToLower()
Dim MIMEType As String = Nothing
Select Case extension
Case ".gif"
MIMEType = "image/gif"
Case ".jpg", ".jpeg", ".jpe"
MIMEType = "image/jpeg"
Case ".png"
MIMEType = "image/png"
Case Else
'無效的文件類型上傳... 顯示錯誤訊息...
Exit Sub
End Select
'與資料庫連接並且將一筆新記錄插入到Products表格中
Using myConnection As New SqlConnection(ConfigurationManager.ConnectionStrings("ImageGalleryConnectionString").ConnectionString)
Const SQL As String = "INSERT INTO [Pictures] ([Title], [MIMEType], [ImageData]) VALUES (@Title, @MIMEType, @ ImageData)"
Dim myCommand As New SqlCommand(SQL, myConnection)
myCommand.Parameters.AddWithValue("@Title", PictureTitle.Text.Trim())
myCommand.Parameters.AddWithValue("@MIMEType", MIMEType)
'把FileUpload控制項的InputStream載入到位元組數組中
Dim imageBytes(UploadedFile.PostedFile.InputStream.Length) As Byte
UploadedFile.PostedFile.InputStream.Read(imageBytes, 0, imageBytes.Length)
myCommand.Parameters.AddWithValue("@ImageData", imageBytes)
myConnection.Open()
myCommand.ExecuteNonQuery()
myConnection.Close()
End Using
End Sub
在此,這個事件處理器首先確保已經上傳一個檔案。然後,它根據被上傳的檔案的副檔名來決定MIME類型。
上面最值得注意的是那些設定@ImageData參數的程式碼部分。首先,建立一個名為imageBytes的位元組數組並且使其長度為被上傳的檔案對應的InputStream。然後,從InputStream中使用Read方法把二進位內容填入這個位元組數組。請注意,正是這個位元組數組被指定為@ImageData的值。