有許多資料都是未結構化的,如文字檔、圖檔、影音檔。這些資料一般統稱二進位大型物件(BLOB)。 若將這些未結構化的資料儲存在資料庫外面,管理上往往造成許多困擾,所以在 SQL 裡,也提供了 varbinary(max) 型別來存放 BLOG 資料。
在 SQL Server 2008 新增了 FILESTREAM 功能,可以讓 SQL Server 直接存取位於檔案系統上的非結構化資料。 它的方式是使用 varbinary(max) 型別,並整合 NTFS 檔案系統,當你存取 BLOB 資料時,實際上 SQL Server 是存取在檔案系統上資料,而不是存取位於資料庫中的資料。
至於該使用哪一種來作業,來存取 BLOG 資料呢?MSDN 提到: 若儲存的物件平均大於 1 MB,請使用 FILESTREAM varbinary(max); 如果是較小的物件,直接使用 varbinary(max) 將物件資料儲存在資料庫之中通常會提供較好的資料流處理效能。
使用 CLR 存取檔案
以往要由資料庫存取檔案系統,必須透透 CLR 才可以,所以在認識 FILESTREAM varbinary(max) 之前,我們先來看看,如何在預存程序中,透過 CLR 去存取檔案系統。
1. 撰寫 CLR 組件
建立儲存資料表
-- CREATE TABLE [dbo].[Documents]( [SerNum] [int] IDENTITY(1,1) NOT NULL, [DocId] [uniqueidentifier] NOT NULL, [FileData] [varbinary](max) NULL, [FileName] [nvarchar](100) NULL, [CreatedTime] [datetime] NULL, )
撰寫組件
using System; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; using System.Collections; using System.Collections.Generic; using System.IO; public partial class StoredProcedures { /// <summary> /// 將指定的檔案存入資料庫 /// </summary> /// <param name="filepath">要存入的檔案</param> [Microsoft.SqlServer.Server.SqlProcedure()] public static void WriteToDB_FromFile(string filepath) { FileInfo file = new FileInfo(filepath); string filename = file.Name; byte[] filedata = ReadFile(file.FullName); string connstr = "context connection = true"; using (SqlConnection conn = new SqlConnection(connstr)) { SqlCommand oCmd = conn.CreateCommand(); oCmd.CommandType = CommandType.Text; oCmd.CommandText = "INSERT Documents(DocId, FileName, FileData) values (NEWID(), @FileName, @FileData)"; oCmd.Parameters.Add("@FileName", SqlDbType.NVarChar).Value = filename; oCmd.Parameters.Add("@FileData", SqlDbType.VarBinary).Value = filedata; try { conn.Open(); oCmd.ExecuteNonQuery(); } catch (Exception ex) { throw new Exception(ex.Message); } } } /// <summary> /// 將資料庫中的資料轉出到指定的資料夾 /// </summary> /// <param name="DocId">要轉出的資料</param> /// <param name="OutputFolder">指定的資料夾</param> [Microsoft.SqlServer.Server.SqlProcedure()] public static void WriteToFile_FromDB(string DocId, string OutputFolder) { if (!Directory.Exists(OutputFolder)) { Directory.CreateDirectory(OutputFolder); //若建立資料夾 } string connstr = "context connection = true"; using (SqlConnection conn = new SqlConnection(connstr)) { SqlCommand oCmd = conn.CreateCommand(); oCmd.CommandType = CommandType.Text; oCmd.CommandText = "Select * from Documents where DocId=@DocId"; oCmd.Parameters.Add("@DocId", SqlDbType.UniqueIdentifier).Value = new Guid(DocId); try { conn.Open(); SqlDataReader reader = oCmd.ExecuteReader(); if (reader.Read()) { byte[] filedata = reader["FileData"] as byte[]; string sFilePath = OutputFolder + @"\\" + reader["FileName"].ToString(); FileStream fs = new FileStream(sFilePath, FileMode.OpenOrCreate, FileAccess.Write); fs.Write(filedata, 0, filedata.Length); fs.Close(); } } catch (Exception ex) { Console.WriteLine(ex.Message); } } } public static byte[] ReadFile(string filePath) { FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read); BinaryReader reader = new BinaryReader(stream); byte[] data = reader.ReadBytes((int)stream.Length); reader.Close(); stream.Close(); return data; } };
2. 載入組件
2.1 啟用 CLR 功能
在 SQL Server 中, CLR 功能預設是停用的,若要啟用 CLR 整合,請使用 sp_configure 預存程序的 clr enabled 選項
sp_configure 'clr enabled', 1; GO RECONFIGURE; GO
2.2 設定 TRUSTWORTHY 屬性
TRUSTWORTHY 屬性是用來指定 SQL Server 執行個體是否信任資料庫及其中的內容。
ALTER DATABASE Northwind SET TRUSTWORTHY ON;
2.3 設定安全性原則
使用 CLR 執行的程式,都得遵守它的安全模型,稱為程式碼存取安全性(Code Access Security, CAS)。 詳細介紹請參考 MSDN : CLR 整合程式碼存取安全性
我們必須在建立組件時指定組件的權限集合。 有三個權限集合:SAFE、EXTERNAL_ACCESS 、 UNSAFE ,預設值為 SAFE 。 因為我們要讀寫檔案,所以必須將權限集合設定成 UNSAFE 。
CREATE ASSEMBLY MySqlLib FROM 'D:\MySqlLib\bin\Debug\MySqlLib.dll' WITH PERMISSION_SET = UNSAFE
3. 建立預存程序
--將檔案寫入DB的預存程序 CREATE PROCEDURE usp_WriteToDB_CLR @FilePath nvarchar(255) AS EXTERNAL NAME MySqlLib.StoredProcedures.WriteToDB_FromFile; --讀取DB資料再寫入檔案的預存程序 CREATE PROCEDURE usp_WriteToFile_CLR @DocId nvarchar(100), @DirPath nvarchar(255) AS EXTERNAL NAME MySqlLib.StoredProcedures.WriteToFile_FromDB;
4. 執行預存程序
--將圖檔寫入資料庫 EXEC usp_WriteToDB_CLR 'D:\CapturePic\sql-clr-udf-1.png'; EXEC usp_WriteToDB_CLR 'D:\CapturePic\sql-clr-udf-3.png';
--將資料庫中的資料轉出至指定目錄 EXEC usp_WriteToFile_CLR '54124D7E-8E45-4AB3-8853-B935B233F5D3','D:\Images'
使用 Filestream
前面提過,Filestream 是 varbinary(max) 的另一種型式,它會將資料直接使用檔案系統儲存,而不是放在資料庫之中。
啟用 FileStream
要啟用 FileStream 功能,共必須完成以下二個步驟:
1. 啟用 FILESTREAM 的檔案 I/O 資料流存取
在 SQL 的組態管理員(SQL Server Configuration Manager)工具中,點選你的 SQL 執行個體。 並按一下 [啟用 FILESTREAM 的檔案 I/O 資料流存取] 和 [允許遠端用戶端具有 FILESTREAM 資料的資料流存取權]。
2. 設定 filestream_access_level
/* filestream_access_level 選項值之說明: */ -- 0 : 針對這個執行個體停用 FILESTREAM 支援。 -- 1 : 針對 Transact-SQL 存取啟用 FILESTREAM。 -- 2 : 針對 Transact-SQL 和 Win32 資料流存取啟用 FILESTREAM。 EXEC sp_configure filestream_access_level, 2 RECONFIGURE
執行完以上步驟,再重新啟動 SQL Server 服務即可。
建立 FileGroup
要使用 FileStream ,除了必須啟用 FileStream 功能外,還得建立 FileGroup 。
FileGroup 就是 FileStream 真正要存放資料的資料庫檔案(database file),然而它實際上並不是一個檔案,而是一個目錄,用來存放 FileStream 的資料檔。 一個 FileGroup 只能有一個存放資料的資料庫檔案。
下列程式碼示範如何建立 FileGroup
ALTER DATABASE Northwind ADD FILEGROUP fg_Documents CONTAINS FILESTREAM; ALTER DATABASE Northwind ADD FILE ( NAME = 'f_Documents' ,FILENAME = 'D:\Documents' ) TO FILEGROUP fg_Documents;
建立完成後,你可以在資料庫的屬性視窗中看到其對於 FileSteram 的支援。
同時你也可以檔案系統中看到系統為你建立的目錄。
- The $FSLOG directory acts as the Filestream data's transaction log.
- The Filestream.hdr file stores metadata about the Filestream filegroup.
- All other directories with GUID names, such as 09A42544-450A-4932-B25F-5E33F117C179, are the directories that store the actual data.
若要移除 FILEGROUP ,請參考以下步驟
ALTER DATABASE [Northwind] REMOVE FILE [f_Documents] GO ALTER DATABASE [Northwind] REMOVE FILEGROUP [fg_Documents] GO
建立 Table
When the filegroup has been added, varbinary(max) Filestream columns can be created. For a table to contain Filestream columns, it must have a uniqueidentifier column marked with the property ROWGUIDCOL and having a unique constraint defined on it. The following code sample shows how to add a ROWGUIDCOL to the FileDocuments table:
要使用 FILESTREAM 資料行,該資料表必須要有一個欄位,符合以下屬性:
- 非 NULL
- 唯一
- 使用 ROWGUID 型別
- 具有 ROWGUIDCOL 屬性
CREATE TABLE [dbo].[Documents]( [SerNum] [int] IDENTITY(1,1) NOT NULL, [DocId] [uniqueidentifier] ROWGUIDCOL NOT NULL UNIQUE, [FileData] [varbinary](max) FILESTREAM NOT NULL, [OriFileName] [nvarchar](100) NULL, [CreatedTime] [datetime] NULL, )
沒有留言:
張貼留言