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 ![]()
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 ! ![]()