Già in passato mi era capitato di affrontare il discorso di esportazione di stringhe o fragments XML su file, ma la mia pelandronaggine non mi aveva ancora permesso di affrontare il discorso relativo ai Binary Large Objects (BLOBs).
Prendendo spunto da un post apparso ieri su microsoft.public.it.sql, ho provato ad analizzare la questione giungendo ad una soluzione che vorrei proporre.
Teo domandava:
Ho delle immagini in una tabella su sql server 2005 express.
E' possibile vederle da SqlServer?
SQL Server 2005 non offre alcun tool per la visualizzazione di immagini contenute in un database, pertanto occorre "ingegnarsi" scrivendo una piccola applicazione client oppure sfruttando l'integrazione con il CLR per realizzare ad esempio una stored procedure che permetta il salvataggio su disco delle immagini.
Ovviamente ho scelto la seconda strada
Prima di tutto ho creato la tabella dbo.Students nel mio database di lavoro, popolandola con 1 riga di esempio:
USE Work;
GO
/* Definisto la tabella dbo.Students */
CREATE TABLE dbo.Students(
StudentID int NOT NULL IDENTITY PRIMARY KEY,
FirstName varchar(10) NOT NULL,
LastName varchar(10) NOT NULL,
Photo varbinary(max) NOT NULL
);
GO
DECLARE @Photo varbinary(max);
/* Carico un file jpeg in una variabile varbinary(max) */
SET @Photo = (
SELECT Q.BulkColumn
FROM OPENROWSET(BULK 'D:\documenti\Immagini\Canon5D.jpg', SINGLE_BLOB) AS Q
);
/* Popolo la tabella dbo.Students */
INSERT dbo.Students VALUES('Lorenzo', 'Benaglia', @Photo);
GO
SELECT *
FROM dbo.Students;
GO
/* Output:
StudentID FirstName LastName Photo
----------- ---------- ---------- ------
1 Lorenzo Benaglia 0xFFD8...
(1 row(s) affected)
*/
A questo punto ho lanciato SQL Server Business Intelligence Development Studio, definendo un nuovo progetto database in C# chiamato "SQLCLRUtility" ed aggiungendo una Stored Procedure chiamata "SaveBLOBTofile" con il seguente codice:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.IO;
public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void SaveBLOBToFile(Byte[] Data, String DestFile)
{
FileStream fs = new FileStream(DestFile, FileMode.Create, FileAccess.Write);
fs.Write(Data, 0, Data.Length);
fs.Close();
SqlContext.Pipe.Send(String.Format("BLOB data successfully saved to file '{0}'", DestFile));
}
};
Come potete vedere il codice è estremamente semplice: la stored procedure riceve in input un array di Bytes ed il nome del file che vogliamo creare.
Il corpo della procedura si limita ad istanziare la classe FileStream e a richiamare il metodo Write per scrivere l'array di bytes nel file passato come argomento.
Tutto qua
Compilando il progetto otterremo l'assembly SQLCLRUtility.dll che andremo a "deployare" manualmente nel database Works.
Apriamo il SQL Server Management Studio, selezioniamo il bottone New Query ed incollamo il seguente script (modificando oppurtunamente il path dell'assembly SQLCLRUtility.dll):
USE Work;
GO
/* Imposto a ON la proprietà TRUSTWORTHY
** in modo da poter accedere alle risorse esterne al database
*/
ALTER DATABASE Work SET TRUSTWORTHY ON;
GO
/* Creo l'assembly */
CREATE ASSEMBLY SQLCLRUtility
AUTHORIZATION dbo
FROM 'C:\...\SQLCLRUtility.dll'
WITH PERMISSION_SET = EXTERNAL_ACCESS;
GO
/* Creo la stored procedure dbo.up_SaveBLOBToFile */
CREATE PROCEDURE dbo.up_SaveBLOBToFile
@Data varbinary(max),
@DestFile nvarchar(256)
WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [SQLCLRUtility].[StoredProcedures].[SaveBLOBToFile];
GO
Ora possiamo richiamare la stored procedure nel seguente modo:
DECLARE @Photo varbinary(max);
SELECT @Photo = Photo
FROM dbo.Students
WHERE StudentID = 1;
EXEC dbo.up_SaveBLOBToFile @Data = @Photo, @DestFile = 'C:\Canon5D.jpg';
/* Output:
BLOB data successfully saved to file 'C:\Canon5D.jpg'
*/
/* Pulizia */
DROP TABLE dbo.Students;
DROP PROCEDURE dbo.up_SaveBLOBToFile;
DROP ASSEMBLY SQLCLRUtility;
!!DEL C:\Canon5D.jpg
Con quest'ultima procedura di esportazione penso di aver coperto un po' tutte le casistiche.