如何: 使用 SQL 文件流






4.39/5 (31投票s)
一篇端到端的文章,介绍如何在 SQL Server 中使用文件流数据类型。本文同时讨论了 SQL Server 和 .NET 方面的内容。
引言
SQL Server 2008 具有文件流数据类型。此数据类型允许用户将内容存储在磁盘上,而不是存储在数据库中作为实际文件。
网上有许多关于此主题的文章,但它们并非都从开发者的角度出发。本文将只介绍足够的信息,帮助您开始使用,并去除所有不必要的干扰。
我会尝试在需要时链接更高级的概念,但本文将重点介绍如何让开发者快速上手使用 SQL Server 文件流数据类型。
背景
本文的读者应了解如何使用 SQL Server Management Studio,了解 SQL Server 中可用的身份验证机制,了解如何创建数据库等。
有很多文章解释何时使用和何时不使用 FileStream
数据类型。MSDN 上的这篇文章是关于此主题的客观参考。
如何配置数据库服务器实例
我们需要做的第一件事是启用服务器实例以接受文件流数据类型。为此,需要遵循以下步骤。
打开 SQL Server 配置管理器
右键单击 SQL Server 服务,然后单击属性
单击 FileStream 选项卡。
启用所有复选框。这些复选框分别启用 SQL Server 内部和客户端的文件流。
Windows 共享名称是包含文件的文件夹的名称。
重新启动服务。
打开 SQL Server Management Studio。
执行。
EXEC sp_configure filestream_access_level, 2
GO
RECONFIGURE
GO
filestream_access_level 2
表示它既可用于 SQL Server,也可用于客户端应用程序。要了解有关文件流访问级别的更多信息,请参阅此链接。
如何创建数据库
下一步是创建一个启用了文件流的新数据库。有许多方法可以做到这一点,但我们将使用图形用户界面(GUI)方法。
打开 SQL Server Management Studio

右键单击数据库,然后单击新建数据库。

在左侧导航窗格中单击文件组。我们需要为文件流创建一个单独的文件组。

在 FileStream
中,添加一个名为 FileStream
的新文件组,并启用默认选项。
单击常规窗格。

添加数据库名称。
单击“添加”按钮以添加另一个数据库文件。
为逻辑名称 FileSystemDB_FileStream
赋值。
将 FileType
选择为 FileStream
数据。
将 FileGroup
选择为步骤 4 中创建的 FileStream
。
填写 Path
。
单击“确定”。
现在,这允许我们创建带有文件流的表。
如何创建表
在此步骤中,我们在数据库中创建一个允许我们访问文件流的表。
CREATE TABLE dbo.PictureTable
(
PkId int Primary Key IDENTITY (1, 1),
Id uniqueidentifier NOT NULL Unique ROWGUIDCOL Default newid(),
Description nvarchar(64) NOT NULL,
FileSummary varbinary(MAX),
FileData varbinary(MAX) FileStream NULL
)
这方面的重要要点如下。Table
有一个 Id 列,类型为 uniqueidentifier
(Guid
),并且是一个 RowGuid
列。这很重要,因为文件将使用此名称存储。
下一个重要列是 FileData
,其类型为 VarBinary
和 FileStream
。这将用于流式传输数据。
使用 T-SQL 进行测试
为了使用 TSql 测试该表,我们插入一行,然后尝试读取同一行。
Insert Into PictureTable([Description],[FileData])
Values('Hello World', Cast('Hello World' As varbinary(max)))
And then select using the statement
SELECT [PkId],[Id],[Description],[FileData],CAST([FileData] _
As varchar(Max)) FROM [PictureTable]
文件实际存储在哪里?
如果您在计算机上找到所有共享文件夹,您将看到在实例配置过程中描述的 Windows 共享文件夹。但是,如果您尝试访问此位置,您将无法这样做。
要查看文件,请转到 SQL Server 的安装位置,然后在数据目录中,默认情况下该目录是 C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA。在此目录中,有一个名为 FileSystemDB_FileStream 的文件夹,它代表数据库中的表。
该表包含一个名为 guid
的文件,每个文件代表存储的行。这是与上面表中创建的行 guid 列具有相同值的 Guid
。
在文本编辑器中打开文件时,您会发现文本 HelloWorld
是该文件的一部分。
数据库方面的操作到此结束,接下来我们将继续介绍如何在 C# 的 .NET 代码中访问文件流。
使用 .NET 访问文件
我们现在进入本文的最后部分。本文介绍了如何使用 .NET 代码读写 SQL 文件流数据。唯一感兴趣的操作是文件读取和文件写入。insert
和 Update
都属于文件写入部分。删除可以使用用于删除行的常规 SQL 语句来完成。
无论是读取数据还是向 SqlFileStream
写入数据,都必须在一个事务中进行。这是因为在常规 SQL 语句中,数据以原子方式返回,而使用 SqlFileStream
数据类型时,数据通过缓冲区从服务器流式传输到客户端,因此没有原子操作。
SqlFileStream
数据类型仅支持集成身份验证。
创建新行
下面的代码行包含在数据库中创建单个条目的代码。让我们来分析一下。
using (TransactionScope transactionScope = new TransactionScope())
{
SqlConnection sqlConnection1 = new SqlConnection("Data Source=.;
Initial Catalog=FileSystemDB;Integrated Security=True");
SqlCommand sqlCommand1 = sqlConnection1.CreateCommand();
sqlCommand1.CommandText = "Insert Into PictureTable
(Description,FileData) values('" + Guid.NewGuid().ToString() +
"',Cast('' As varbinary(Max))); Select FileData.PathName()
As Path From PictureTable Where PkId =@@Identity";
sqlConnection1.Open();
string filePath1 = (string)sqlCommand1.ExecuteScalar();
SqlConnection sqlConnection2 = new SqlConnection("Data Source=.;
Initial Catalog=FileSystemDB;Integrated Security=True");
SqlCommand sqlCommand2 = sqlConnection2.CreateCommand();
sqlCommand2.CommandText = "Select GET_FILESTREAM_TRANSACTION_CONTEXT()
As TransactionContext";
sqlConnection2.Open();
byte[] transactionContext1 =(byte[]) sqlCommand2.ExecuteScalar();
SqlFileStream sqlFileStream1 = new SqlFileStream
(filePath1, transactionContext1, FileAccess.Write);
byte[] fileData = Guid.NewGuid().ToByteArray();
sqlFileStream1.Write(fileData, 0, fileData.Length);
sqlFileStream1.Close();
transactionScope.Complete();
}
第一个语句是 using (TransactionScope transactionScope = new TransactionScope()
),它启动了一个事务范围。
接下来,我们使用集成身份验证打开一个连接,并执行格式为
Insert Into PictureTable (Description ,FileData ) Values(“Some String made using Guid”,
Cast(‘’ as varchar(max))
我们在这里做的是创建一个空文件。这样做是因为我们需要文件的路径来上传文件。
此文件路径在下一条语句中使用
Select FileData.Pathname() As Path From PictureTable where PkId = @@Identity
这会以 UNC 共享的形式返回文件路径的位置。
接下来,我们执行查询 Select GET_FILESTREAM_TRANSACTION_CONTEXT()
,它会返回一个与事务范围对应的事务上下文。文件将在该事务上下文中读取。
接下来,使用文件路径和事务上下文打开一个 SqlFileStream
。之后是简单的将字节数组上传到文件流的代码。
更新数据将是类似的活动,我们可以根据某个主键找到文件路径。
读取数据
读取数据也相当直接。我们找到感兴趣行的文件路径,然后在事务范围内获取事务上下文的实例。使用此上下文打开一个 SqlFileStream
,并从中读取 byte[]
。下面的代码显示了这一点。
using (TransactionScope transactionScope2 = new TransactionScope())
{
SqlConnection sqlConnection3 = new SqlConnection("Data Source=.;
Initial Catalog=FileSystemDB;Integrated Security=True");
SqlCommand sqlCommand3 = sqlConnection3.CreateCommand();
sqlCommand3.CommandText = "Select FileData.PathName() As Path,
GET_FILESTREAM_TRANSACTION_CONTEXT() As TransactionContext
From PictureTable Where PkId = (Select Max(PkId) From PictureTable)";
sqlConnection3.Open();
SqlDataReader reader = sqlCommand3.ExecuteReader();
reader.Read();
string filePath = (string)reader["Path"];
byte[] transactionContext2 = (byte[])reader["TransactionContext"];
SqlFileStream sqlFileStream2 = new SqlFileStream
(filePath, transactionContext2, FileAccess.Read);
byte[] data = new byte[sqlFileStream2.Length];
sqlFileStream2.Read(data, 0, Convert.ToInt16(sqlFileStream2.Length));
Guid valueInserted = new Guid(data);
sqlFileStream2.Close();
}
历史
- 2010 年 11 月 18 日:初始帖子