FILESTREAM 数据

FILESTREAM 存储属性可用于 varbinary(max) 列中存储的二进制 (BLOB) 数据。 在使用 FILESTREAM 之前,存储二进制数据需要经过特殊处理。 非结构化数据(例如文本文档、图像和视频)通常存储在数据库外部,因此难以管理。

注意

你必须安装 .NET Framework 3.5 SP1(或更高版本)才能使用 SqlClient 处理 FILESTREAM 数据。

varbinary(max) 列上指定 FILESTREAM 特性会致使 SQL Server 将数据存储在本地 NTFS 文件系统中,而不是存储在数据库文件中。 虽然数据是单独存储的,但你可以使用所支持的相同 Transact-SQL 语句来处理存储在数据库中的 varbinary(max) 数据。

SqlClient 对 FILESTREAM 的支持

适用于 SQL Server 的 .NET Framework 数据提供程序 (System.Data.SqlClient) 支持使用在 System.Data.SqlTypes 命名空间中定义的 SqlFileStream 类来读写 FILESTREAM 数据。 SqlFileStream 继承自 Stream 类,该类提供了用于读写数据流的方法。 从流中读取会将数据从流传输到数据结构(如字节数组)中。 写入数据会将数据从数据结构传输到流中。

创建 SQL Server 表

下列 Transact-SQL 语句将创建一个名为 employees 的表并插入一行数据。 启用 FILESTREAM 存储后,可以将此表与下面的代码示例结合使用。

CREATE TABLE employees
(
  EmployeeId INT  NOT NULL  PRIMARY KEY,
  Photo VARBINARY(MAX) FILESTREAM  NULL,
  RowGuid UNIQUEIDENTIFIER  NOT NULL  ROWGUIDCOL
  UNIQUE DEFAULT NEWID()
)
GO
Insert into employees
Values(1, 0x00, default)
GO

示例:读取、覆盖和插入 FILESTREAM 数据

下面的示例演示如何从 FILESTREAM 读取数据。 该代码获取文件的逻辑路径,将 FileAccess 设置为 Read,并将 FileOptions 设置为 SequentialScan。 然后,该代码将 SqlFileStream 中的字节读取到缓冲区中。 然后,将字节写入控制台窗口。

该示例还演示了如何将数据写入到 FILESTREAM(其中现有的所有数据将被覆盖)。 该代码获取文件的逻辑路径,并创建 SqlFileStream,将 FileAccess 设置为 Write,将 FileOptions 设置为 SequentialScan。 将一个字节写入 SqlFileStream,替换文件中的所有数据。

示例还演示了如何通过使用 Seek 方法将数据附加到 FILESTREAM 文件的结尾,以将数据写入到其中。 该代码获取文件的逻辑路径,并创建 SqlFileStream,将 FileAccess 设置为 ReadWrite,将 FileOptions 设置为 SequentialScan。 此代码使用 Seek 方法查找到文件的末尾,并向现有文件追加一个字节。

using System;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Data;
using System.IO;

namespace FileStreamTest
{
    class Program
    {
        static void Main(string[] args)
        {
            SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder("...");
            ReadFileStream(builder);
            OverwriteFileStream(builder);
            InsertFileStream(builder);

            Console.WriteLine("Done");
        }

        private static void ReadFileStream(SqlConnectionStringBuilder connStringBuilder)
        {
            using (SqlConnection connection = new SqlConnection(connStringBuilder.ToString()))
            {
                connection.Open();
                SqlCommand command = new SqlCommand("SELECT TOP(1) Photo.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT() FROM employees", connection);

                SqlTransaction tran = connection.BeginTransaction(IsolationLevel.ReadCommitted);
                command.Transaction = tran;

                using (SqlDataReader reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        // Get the pointer for the file
                        string path = reader.GetString(0);
                        byte[] transactionContext = reader.GetSqlBytes(1).Buffer;

                        // Create the SqlFileStream
                        using (Stream fileStream = new SqlFileStream(path, transactionContext, FileAccess.Read, FileOptions.SequentialScan, allocationSize: 0))
                        {
                            // Read the contents as bytes and write them to the console
                            for (long index = 0; index < fileStream.Length; index++)
                            {
                                Console.WriteLine(fileStream.ReadByte());
                            }
                        }
                    }
                }
                tran.Commit();
            }
        }

        private static void OverwriteFileStream(SqlConnectionStringBuilder connStringBuilder)
        {
            using (SqlConnection connection = new SqlConnection(connStringBuilder.ToString()))
            {
                connection.Open();

                SqlCommand command = new SqlCommand("SELECT TOP(1) Photo.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT() FROM employees", connection);

                SqlTransaction tran = connection.BeginTransaction(IsolationLevel.ReadCommitted);
                command.Transaction = tran;

                using (SqlDataReader reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        // Get the pointer for file
                        string path = reader.GetString(0);
                        byte[] transactionContext = reader.GetSqlBytes(1).Buffer;

                        // Create the SqlFileStream
                        using (Stream fileStream = new SqlFileStream(path, transactionContext, FileAccess.Write, FileOptions.SequentialScan, allocationSize: 0))
                        {
                            // Write a single byte to the file. This will
                            // replace any data in the file.
                            fileStream.WriteByte(0x01);
                        }
                    }
                }
                tran.Commit();
            }
        }

        private static void InsertFileStream(SqlConnectionStringBuilder connStringBuilder)
        {
            using (SqlConnection connection = new SqlConnection(connStringBuilder.ToString()))
            {
                connection.Open();

                SqlCommand command = new SqlCommand("SELECT TOP(1) Photo.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT() FROM employees", connection);

                SqlTransaction tran = connection.BeginTransaction(IsolationLevel.ReadCommitted);
                command.Transaction = tran;

                using (SqlDataReader reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        // Get the pointer for file
                        string path = reader.GetString(0);
                        byte[] transactionContext = reader.GetSqlBytes(1).Buffer;

                        using (Stream fileStream = new SqlFileStream(path, transactionContext, FileAccess.ReadWrite, FileOptions.SequentialScan, allocationSize: 0))
                        {
                            // Seek to the end of the file
                            fileStream.Seek(0, SeekOrigin.End);

                            // Append a single byte
                            fileStream.WriteByte(0x01);
                        }
                    }
                }
                tran.Commit();
            }

        }
    }
}

关于另一个示例,请参见如何将二进制数据存储和提取到文件流列中

SQL Server 文档资源

FILESTREAM 的完整文档位于 SQL Server 文档的以下各节中。

主题 说明
FILESTREAM (SQL Server) 介绍何时使用 FILESTREAM 存储以及它如何将 SQL Server 数据库引擎与 NTFS 文件系统集成。
为 FILESTREAM 数据创建客户端应用程序 介绍用于处理 FILESTREAM 数据的 Windows API 函数。
FILESTREAM 与其他 SQL Server 功能 提供将 FILESTREAM 数据与 SQL Server 的其他功能一起使用时的注意事项、准则和限制。

另请参阅