使用SQL Server 2008的FILESTREAM特性管理文件
原創【51CTO快譯】
SQL Server的FILESTREAM(文件流)特性簡化了基于文件的數據(如圖像)和關系數據同步的過程。
幾乎所有的應用程序都需要某種類型的數據集,至少在檢索某些數據和在用戶界面中顯示時要用到,通常,應用程序會使用到結構化數據和非結構化數據,這樣就引入了極大的挑戰,你不得不在一個事務中創建、更新、刪除和讀取這些完全不同的數據類型,當結構化數據駐留在關系數據庫中而非結構化數據卻存儲在文件系統中時,這個問題尤為嚴重。SQL Server 2008新的FILESTREAM(文件流)特性可以幫助解決這個問題,它讓你可以將非結構化數據存儲在文件系統中,但仍然保持了事務的完整性,本文探討FILESTREAM(文件流)的特性和優點,以及如何運用它幫助你對非結構化數據進行更好地控制。
非結構化數據選項
在SQL Server 2005中,構建一個既依賴于結構化(關系)數據有依賴于非結構化(無關系)數據時,你有兩個選擇:
在數據庫中存儲結構化數據,在一個專用的存儲中存儲非結構化數據,如文件系統和文件服務器,雖然這種方法成本合算,但它引入了額外的復雜度,因為你需要跨關系和非關系系統管理事務的完整性。
將結構化數據和非結構化數據都存儲在數據庫中,多年以來,數據庫一直都支持存儲非關系數據,如二進制大對象,或BLOB,SQL Server稱之為varbinary數據類型,雖然在數據庫中存儲這種數據是很方便的,但成本費用會更高,所需的磁盤空間更多,存儲和檢索時間更長,對應用程序的整體性能也會有負面影響。
在SQL Server 2008中,新的FILESTREAM(文件流)特性和varbinary列配合,你可以在服務器的文件系統上存儲真實的數據,但可以在數據庫上下文內管理和訪問,這個特性讓SQL Server不僅可以維護好數據庫內記錄的完整性,也能夠維護好數據庫記錄和外部文件之間的完整性。因為這個特性是在現有的varbinary(max)數據類型之上實現的,開發人員可以輕易地用上這個特性,不用對應用程序的架構進行改動。
什么時候使用FILESTREAM(文件流)
在下列任一情景下你都可以考慮使用FILESTREAM(文件流):
當你存儲平均大小不低于1MB的BLOB數據類型時。
當你需要更快、只讀訪問來自應用程序的數據時。
當你想直接從應用程序的中間層代碼訪問BLOB時。
當你需要為單個數據庫事務在數據庫中存儲非關系數據和關系數據時。
啟用FILESTREAM(文件流)
默認情況下,FILESTREAM(文件流)特性是被禁用了的,因此在使用之前,你必須按照下面的步驟配置服務器和數據庫實例:
1、要啟用服務器實例上的FILESTREAM(文件流),打開SQL Server配置管理器,在SQL Server服務上點擊右鍵,然后點擊打開,你會看到一串服務器,在你想要啟用FILESTREAM(文件流)的SQL Server實例上點擊右鍵,從右鍵菜單中選擇“屬性”,切換到FILESTREAM(文件流)標簽,檢查“為Transact-SQL訪問啟用FILESTREAM(文件流)”選項,參考圖1 ,你也可以在這個標簽頁為文件I/O流訪問啟用FILESTREAM(文件流)。
圖1 啟用FILESTREAM(文件流)-在為數據庫實例配置使用FILESTREAM(文件流)訪問之前必須為想要的SQL Server實例啟用FILESTREAM(文件流)
2、要為數據庫實例啟用FILESTREAM(文件流),執行系統存儲過程sp_configure,并設置filestream_access_level參數的值為2,如下:
EXEC sp_configure filestream_access_level, 2 GO RECONFIGURE GO |
filestream_access_level參數有效的值包括:
◆ 0 在該實例上禁用FILESTREAM(文件流),這是默認值。
◆ 1 為Transact-SQL訪問啟用FILESTREAM(文件流)
◆ 2 為Transact-SQL和Win32流訪問啟用FILESTREAM(文件流)
完成服務器和數據庫實例配置后,接下來是創建存儲數據的真實數據庫,因為FILESTREAM(文件流)是專門為存儲在文件系統上的二進制數據創建的,使用CREATE DATABASE語句時,專門創建一個FILEGROUP標記為流:
CREATE DATABASE FILESTREAMExample |
接下來,創建一個表,將它的一個列指派為VARBINARY(MAX) FILESTREAM數據類型:
CREATE TABLE Product |
前面的表定義指定Picture列為varbinary(max)類型,并啟用了FILESTREAM(文件流)屬性,注意:凡是有FILESTREAM(文件流)列的表必須要包括一個非空唯一性ROWGUID列。
所有存儲在Picture列中的二進制數據都不能通過文件系統訪問,訪問這個二進制數據的唯一方法是通過標準的CRUD (INSERT,UPDATE和 DELETE)SQL語句,下面的例子是向Product表中插入一行數據:
INSERT INTO Product VALUES(1, 'Bicycle', 0x00, default) |
插入的新行ProductID等于1,Name包括Bicycle,Picture列為NULL,RowGuid列包括一些默認的GUID值,現在你可以在.NET程序中檢索這一行,當然也可以覆蓋和擴展它的內容。
#p#
使用FILESTREAM(文件流)寫入數據
在這個例子中,假設用戶產生了一些輸入,要將這些輸入內容轉換成字節數組,并將其存儲在Product表的Picture列中,接下來創建一個Visual C#視窗應用程序,命名為FileStreamExample,在新項目的默認表單上,添加一個按鈕,命名為btnWriteFile,一個名叫txtInput的文本輸入框(TextBox),一個命名為lstResults的列表框(ListBox),然后,在按鈕上雙擊創建一個click事件處理器,包括清單1中的代碼。
清單1 使用FILESTREAM存儲數據
private void btnWriteFile_Click(object sender, EventArgs e) { string connectionString = ConfigurationManager.ConnectionStrings ["fileStreamDB"].ConnectionString; using (SqlConnection connection = new SqlConnection(connectionString)) { connection.Open(); SqlCommand command = new SqlCommand(); command.Connection = connection; //Get the PathName of the File from the database command.CommandText = "SELECT Picture.PathName(), " + "GET_FILESTREAM_TRANSACTION_CONTEXT() FROM Product " + "WHERE ProductID = 1"; SqlTransaction transaction = connection.BeginTransaction (IsolationLevel.ReadCommitted); command.Transaction = transaction; using (SqlDataReader reader = command.ExecuteReader()) { while (reader.Read()) { string path = reader.GetString(0); SqlFileStream stream = new SqlFileStream(path, (byte[])reader.GetValue(1), FileAccess.Write, FileOptions.SequentialScan, 0); string contents = txtInput.Text; stream.Write((System.Text.Encoding.ASCII.GetBytes(contents)), 0, contents.Length); stream.Close(); } } transaction.Commit(); } MessageBox.Show("File contents successfully written"); }
它從app.config使用ConfigurationManager.ConnectionStrings屬性檢索連接字符串:
string connectionString = ConfigurationManager.ConnectionStrings |
連接字符串存儲在app.config中,如下:
<?xml version="1.0" encoding="utf-8" ?> |
接下來它打開一個到數據庫的連接,為SqlCommand對象分配屬性值,然后以ProductID=1為條件檢索Products表:
command.Connection = connection; |
這個SQL語句使用了新的函數GET_FILESTREAM_TRANSACTION_CONTEXT ()檢索當前運行的會話事務,你可以綁定FILESTREAM(文件流)文件系統操作到該事務上,但這個事務必須是已經啟動了的,并且也不能被異常終止或提交,當沒有明確啟動的事務可用的,它返回NULL值。
因此,下面的代碼調用SqlConnection.BeginTransaction()方法創建一個新的SqlTransaction對象,并將其分配給SqlCommand對象:
SqlTransaction transaction = |
至此,清單1啟動ExecuteReader()方法執行SQL語句,執行完查詢后,返回文件流的路徑,并向它分配一個本地變量:
string path = reader.GetString(0); |
你需要一個流寫入到文件中,因此,接下來要創建一個SqlFileStream類的實例,提供路徑、事務上下文、文件訪問目錄、文件選項一覽表和分配大小:
SqlFileStream stream = new SqlFileStream(path, |
SqlFileStream類是一個新類(SQL Server 2008中才引入的),它提供了以字節序列方式訪問FILESTREAM(文件流)列的方法,表1對SqlFileStream類暴露在外的屬性做了一個簡單的描述。
表1 SqlFileStream類屬性
接下來清單1開始檢索用戶的輸入,轉換成字節數組,然后寫入到文件流中:
string contents = txtInput.Text; |
最后,清單1調用SqlTransaction.Commit()方法提交事務:
transaction.Commit(); |
以上就是往由數據庫管理的啟用了FILESTREAM(文件流)特性的文件的基本過程,既然已經知道如何寫入FILESTREAM列,那從FILESTREAM列讀取就簡單了。
#p#
使用FILESTREAM讀取數據
在C#項目的默認表單上,添加一個按鈕,命名為btnReadFile,在click事件中插入清單2中的代碼。
清單2 使用FILESTREAM讀取數據。這個click事件處理程序從數據庫讀取FILESTREAM列中的內容。
{
string connectionString = ConfigurationManager.ConnectionStrings
["fileStreamDB"].ConnectionString;
using (SqlConnection connection = new
SqlConnection(connectionString))
{
connection.Open();
SqlCommand command = new SqlCommand();
command.Connection = connection;
//Get the PathName of the File from the database
command.CommandText = "SELECT Picture.PathName(), "
+ "GET_FILESTREAM_TRANSACTION_CONTEXT() FROM Product "
+ "WHERE ProductID = 1";
SqlTransaction transaction =
connection.BeginTransaction(IsolationLevel.ReadCommitted);
command.Transaction = transaction;
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
string path = reader.GetString(0);
SqlFileStream stream = new SqlFileStream(path,
(byte[])reader.GetValue(1),FileAccess.Read,
FileOptions.SequentialScan, 0);
lstResults.Items.Clear();
int length = (int) stream.Length;
byte[] contents = new byte[length];
stream.Read(contents,0,length);
string results = System.Text.Encoding.ASCII.GetString
(contents);
lstResults.Items.Add(results);
stream.Close();
}
}
transaction.Commit();
}
}
為了簡單起見,我只討論與前面不同的代碼,當你創建一個SqlFileStream時,你需要指出你打開的文件流:
SqlFileStream stream = new SqlFileStream(path, |
接下來,讀取文件流的內容,轉換成字節數組,再轉換成字符串,最后在列表框(ListBox)中顯示出來:
int length = (int) stream.Length; |
當你運行這個應用程序時,你會看到一個類似于圖2的屏幕,當你點擊“寫入文件”按鈕時,應用程序把文本框(TextBox)中的內容寫入到文件流中;當你點擊“讀取文件”按鈕時,應用程序從文件流讀取內容,將其顯示在列表框(ListBox)中。
圖2 示例項目-通過使用SqlFileStream類讀取和寫入FILESTREAM列中的內容
接下來的例子顯示如何擴展現有數據庫中的文件流。
使用FILESTREAM追加數據
增加一個命令按鈕,命名為btnAppendFile,使用清單3中的代碼作為它的click事件處理程序代碼。
清單3 使用FILESTREAM追加數據
private void btnAppendFile_Click(object sender, EventArgs e)
{
string connectionString =
ConfigurationManager.ConnectionStrings
["fileStreamDB"].ConnectionString;
using (SqlConnection connection = new
SqlConnection(connectionString))
{
connection.Open();
SqlCommand command = new SqlCommand();
command.Connection = connection;
//Get the PathName of the File from the database
command.CommandText = "SELECT Picture.PathName(), "
+ "GET_FILESTREAM_TRANSACTION_CONTEXT() FROM Product "
+ "WHERE ProductID = 1";
SqlTransaction transaction =
connection.BeginTransaction(IsolationLevel.ReadCommitted);
command.Transaction = transaction;
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
string path = reader.GetString(0);
SqlFileStream stream = new SqlFileStream(path,
(byte[])reader.GetValue(1), FileAccess.ReadWrite,
FileOptions.SequentialScan, 0);
stream.Seek(0, SeekOrigin.End);
string contents = txtInput.Text;
stream.Write((System.Text.Encoding.ASCII.GetBytes
(contents)), 0, contents.Length);
stream.Close();
}
}
transaction.Commit();
}
MessageBox.Show("File contents successfully appended");
}
這次當你實例化SqlFileStream對象時,將文件訪問權設為ReadWrite,因此你可以讀取和寫入文件流。
SqlFileStream stream = new SqlFileStream(path, |
然后移動文件流的指針到文件末尾,這樣你就可以追加數據了:
stream.Seek(0, SeekOrigin.End); |
接下來使用SqlFileStream.Write()方法將用戶輸入的內容寫入到文件流中:
stream.Write((System.Text.Encoding.ASCII.GetBytes |
最后調用SqlTransaction.Commit()方法提交事務。
FILESTREAM的優點
這就是全部過程,現在你可以讀取、寫入和追加數據到數據庫管理的文件中了,雖然這個過程可能比使用文件或在BLOB中存儲數據更復雜一些,你會發現使用數據庫來管理文件由許多好處。
◆ 使用單個數據存儲就可以同時訪問非關系和關系數據。
◆ 在數據庫備份和還原期間SQL Server自動包括非關系數據(BLOB)。
◆ 沒有文件大小限制,varbinary(max)數據類型最大不能超過2GB,唯一受限的就是NTFS文件系統上的可用空間。
◆ 你可以在單個事務中同時插入、更新和刪除關系數據和非關系數據。
◆ 使用FILESTREAM效率更高,因為SQL Server不再使用緩沖區內存操作非關系數據(BLOB)。
◆ 你可以使用ADO.NET在中間層代碼直接訪問非關系數據,不用再求值于獨立的API了。
◆ 依賴于文件大小,NTFS文件系統可以比SQL Server更快地保存和檢索大型BLOB。
本文向你展示了如何實現新的FILESTREAM特性,正如你所看到的,當你想在一個事務中同時存儲關系數據和非關系數據時,FILESTREAM提供了一個易于使用的事務編程模型。
原文:Managing Files with SQL Server 2008's FILESTREAM Feature
作者:Thiru Thangarathinam
【51CTO獨家譯稿,合作站點轉載請注明原文譯者和出處為51CTO.com】
【編輯推薦】