Блог инженеров технической поддержки SQL Server. Microsoft. Россия

Блог инженеров технической поддержки SQL Server. Microsoft. Россия

Методы доступа к FILESTREAM.

Мы продолжаем публикацию блогов по FILESTREAM.

В этом блоге мы рассмотрим методы доступа к данным FILESTREAM. Возможно в данной статье для вас не будет ничего нового, но мы решили опубликовать его, чтобы наш разговор о FILESTREAM был логично завершен.

Для доступа к FILESTREAM есть две возможности:

  1. Доступ с использованием языка Transact-SQL.
  2. Доступ из Win API специально предназначенного для такого доступа.

Доступ через Transact-SQL

Здесь все достаточно понятно и прозрачно. Для доступа используются стандартные DML-операторы, SELECT, INSERT, DELETE, UPDATE.

Особенностью операции UPDATE является то, что она не поддерживает частичное обновление файла и как результат выполняется как операция DELETE/INSERT.

Ниже приведены примеры выполнения таких операторов.

INSERT INTO dbo.FS_Records (Document)
VALUES (CAST(‘Yes, it””s work fine’ as varbinary(max)));
GO

SELECT *, cast (fsr.Document as char(100) ) as [Document text]
FROM dbo.FS_Records fsr;
GO

UPDATE FS_Records
SET Document = CAST(‘It is updated column’ as varbinary(max))
WHERE DocId = 2;
 GO

Единственно необычной вещью здесь является функция PathName(). В общем-то, для работы через Transact-SQL она не нужна, но через нее можно получить путь к файлу, который потом может быт передан в метод Win API для доступа к файлу лежащему на файловой системе.

Как было показано в предыдущих статьях эта функция не возвращает реального пути, но она дает точку перенаправления через драйвер RsFx0300xx к SQL Server-у.

Доступ через специальный Win API

Далее приводится пример доступа к FILESTREAM-файлам через Win API.

Пример написан на языке C# и имеет комментарии для каждой строки.

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

namespace FILESTREAM

{

  class Program

    {

   static void Main(string[] args)

       {

    string DocId = args[0]; //Параметр передаваемый из командной строки.

    SqlConnection sqlConnection = new SqlConnection(“Integrated Security=true;server=localhost\\SQL2014”); //Устанавливаем соединение к серверу

    SqlCommand sqlCommand = new SqlCommand();

    sqlCommand.Connection = sqlConnection;

    try

          {

            sqlConnection.Open();

     // Первое, что необходимо сделать, это получить путь к файлу SQL FILESTREAM BLOBа, через который приложение будет получать доступ к данным хранящимся в этом SQL FILESTREAM BLOB.
     // Для получения пути к файлу используется функция PathName().

     sqlCommand.CommandText = “SELECT Document.PathName() FROM FileStreamTest.dbo.FS_Records where DocId =” + DocId; //DocId – это аргумент, переданный из командной строки. 

     String filePath = null;

     Object pathObj = sqlCommand.ExecuteScalar();

     // Путь будет представлять строку типа
     // “\\\\ALEXAK-WS1\\SQL2014_FileStream\\v02-A60EC2F8-2B24-11DF-9CC3-AF2E56D89593\\FileStreamTest\\dbo\\FS_Records\\Document\\1895BA5E-F64E-45C5-9E22-65D225A69BD9\\VolumeHint-HarddiskVolume6”
     // С помощью даной строки будет осуществлено перенаправление запроса через общий ресурс \\ALEXAK-WS1\SQL2014_FileStream на реальный файл, как это описано в наших предыдущих блогах.

     if(pathObj==null)

       {

       throw new System.Exception(“There is no such Documents”);

       }

        if (DBNull.Value != pathObj) 

           {              

            filePath = (string)pathObj;

            }

        else

           {

            throw new System.Exception(“Document.PathName() failed to read the path name for the Document column.”);

            }

     // Далее необходимо получить контекст транзакции.
     // Все FILESTREAM BLOB операции ДОЛЖНЫ происходят внутри транзакций для того, чтобы избежать конфликтов с другими транзакциями,
     // например c транзакциями MARS, происходящими внутри специфических batch scoped транзакций.

     SqlTransaction fsTran = sqlConnection.BeginTransaction(“FileStreamTran”);

     sqlCommand.Transaction = fsTran;

     //Получаем контекст транзакции

     sqlCommand.CommandText =“SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()”;

     Object obj = sqlCommand.ExecuteScalar();

     byte[] txContext = (byte[])obj;

     //Следующий шаг – получить handle, который может быть передан в Win32 FILE APIs.
     //sqlFileStream1 -это не Handle, но это объект класса SqlFileStream и он содержит нужный Handle
     //Обратите внимание, что мы открыли FILESTREAM только для чтения, но могли отрыть его для Read, ReadWrite, Write 

     SqlFileStream sqlFileStream1 = new SqlFileStream(filePath, txContext, FileAccess.Read);

     // Создаем буфер для чтения данных из файла

     byte[] data = new byte[sqlFileStream1.Length];

     //Читаем данные в буфер

     sqlFileStream1.Read(data, 0, Convert.ToInt16(sqlFileStream1.Length));

     //Конвертируем данные из буфера в текстовую строку

     string str = System.Text.Encoding.UTF8.GetString(data);

     //Вывод на экран

     Console.WriteLine(“Here is srting”);

     Console.WriteLine(str);

     //Поскольку работа с файлом завершена, FILESTREAM доступ должен быть закрыт.

     sqlFileStream1.Close();

     //Завершение транзакции (COMMIT или ROLLBACK)

     sqlCommand.Transaction.Commit();

           }

      catch (System.Exception ex)

           {

     Console.WriteLine(ex.ToString()); //Обработка необработанных исключительных ситуаций

           }

      finally

          {

             sqlConnection.Close();

           }

      return;

      }

   }

}

На этом блоге мы заканчиваем публикации по SQL Server FILESTREAM и надеемся, что вы нашли здесь много нового для себя.

Александр Каленик, Senior Premier Field Engineer (PFE), MSFT (Russia)