Mim

Select * from Seb where Sujets in (SQL Server 2008, ADO.NET, Visual Studio 2008)

SqlFileStream

Avec l'arrivée du Framework 3.5 SP1 béta, de nouvelles classes ont fait leur apparition.

Notamment un ensemble de classes pour gérer le nouveau type SQL SERVER 2008 : FILESTREAM.

Lors de mes différentes présentations sur le sujet, j'ai toujours du utiliser une classe encapsulant un appel à une dll C, que je ne présentais pas d'ailleurs :) en expliquant que "bientôt" nous aurions droit à de vrais classes managed pour gérer tout ça

Et bien, c'est le cas; le framework (3.5 SP1 béta) est là !

Maintenant, tout devient (relativement) simple Happy

D'abord le script de création d'une table contenant un type FILESTREAM :

-- 1. Enable FILESTREAM support
EXEC [sp_filestream_configure] @enable_level = 3;

-- 2. Create a database with a File Group that contains FILESTREAM
CREATE DATABASE FileManagement 
ON
PRIMARY ( 
    NAME = FileManagement_Primary,
    FILENAME = 'c:\temp\data\FileManagement.mdf'),
FILEGROUP FileStreamGroup CONTAINS FILESTREAM ( 
    NAME = FileManagement_FileGroup,
    FILENAME = 'c:\temp\data\FileManagement')
LOG ON  ( NAME = FileManagement_Log,
    FILENAME = 'c:\temp\data\FileManagementLog.ldf')
GO

-- 3. Create a Table with FILESTREAM
CREATE TABLE [dbo].[Files] 
(
    FileID uniqueidentifier NOT NULL ROWGUIDCOL PRIMARY KEY,
    FileContents varbinary(max) FILESTREAM DEFAULT(0x)
)
GO

En préambule, on crée une transaction (BIENSUR !)

SqlTransaction tx = conn.BeginTransaction();

J'insère dans ma table, contenant un filestream, une ligne (tout ça pour récupérer un contexte de transaction par la suite)

SqlCommand insertFileCommand = conn.CreateCommand();
insertFileCommand.Transaction = tx;
insertFileCommand.CommandText = "INSERT INTO Files (FileID) VALUES (@FileID)";
Guid newFileID = Guid.NewGuid();
insertFileCommand.Parameters.Add("@FileID", SqlDbType.UniqueIdentifier).Value = newFileID;
insertFileCommand.ExecuteNonQuery();

Je récupère le contexte de transaction :

SqlCommand getPathAndTokenCommand = conn.CreateCommand();
getPathAndTokenCommand.Transaction = tx;
getPathAndTokenCommand.CommandText = "SELECT FileContents.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT() FROM Files WHERE FileID = @FileID";
getPathAndTokenCommand.Parameters.Add("@FileID", SqlDbType.UniqueIdentifier).Value = newFileID;
SqlDataReader tokenReader = getPathAndTokenCommand.ExecuteReader(CommandBehavior.SingleRow);
tokenReader.Read();
SqlString filePathName = tokenReader.GetSqlString(0);
SqlBinary fileToken = tokenReader.GetSqlBinary(1);
tokenReader.Close();

Et maintenant j'utilise ce fameux nouveau type, comme un simple type FileStream (j'ouvre un flux, et j'écris dedans !)

FileStream inputFile = File.OpenRead("TextFile1.txt");
SqlFileStream sqlFile = new SqlFileStream(filePathName.Value, fileToken.Value, System.IO.FileAccess.Write);
byte[] buffer = new byte[512 * 1024]; // 512Kb

int bytesRead = inputFile.Read(buffer, 0, buffer.Length);
while (bytesRead > 0)
{
    sqlFile.Write(buffer, 0, bytesRead);
    bytesRead = inputFile.Read(buffer, 0, buffer.Length);
}

J'oublie pas de tout fermer hein ;)

 

sqlFile.Close();
inputFile.Close();
tx.Commit();
conn.Close();

 

Et voilà !

Bon FILESTREAM ! Applause