Bilder aus dem SQL Server exportieren

Permalink

Im Zuge einer Migration musste ich im SQL Server gespeicherte Bilder als Dateien exportieren. Dabei lagen keine Informationen über den Dateityp oder andere Informationen vor. Trotzdem gibt es Möglichkeiten die üblichen Formate GIF, JPG oder PNG aus dem Binärstring zu identifizieren.

In der hier verwendeten Lösung verwende ich OLE Automation. Natürlich kann man auch jede andere Sprache verwenden. Mit OLE Automation ist aber keine zusätzliche Laufzeitumgebung und kein Compiler nötig, sondern nur das übliche SQL Server Management Studio.

Ausganssituation

Für das bessere Verständnis des Scripts möchte ich hier eine vereinfachte Fassung der Ausgangssituation vorstellen. Diese besteht aus einer Tabelle People mit zwei Spalten Name und Image:

Name Image
Type varchar varbinary
Beispiel Maxime 0xfe56a245934…

Mittels Name soll der Dateiname für das Bild bestimmt werden und in der Spalte Image befindet sich die hexadezimale Darstellung des Portraits.

Vorbereitungen

Um die OLE Automisation im SQL Server nutzen zu können, müsste die zugehörigen Prozeduren erst einmal freigeschaltet werden. Standardmäßig sind die meisten Scriptinginterfaces aus Sicherheitsgründen deaktiviert.

Der erste Schritt besteht darin zu prüfen, wie die aktuelle Einstellung aussieht:

EXEC sp_configure;

Sollte die Zeile Ole Automation Procedures des Ergebnisses beim Wert eine Eins enthalten, ist alles gut. Ist eine solche Zeile nicht enthalten, müssen erst noch die erweiterten Optionen aktiviert werden:

sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO

Anschließend kann man die OLE-Funktionen auch aktivieren:

sp_configure 'Ole Automation Procedures', 1;
GO
RECONFIGURE;
GO

Damit sind alle Vorbereitungen abgeschlossen und ich kann zum eigentlichen Script übergeben.

Exportieren der Bilder

Das Script besteht im Grunde aus einem Cursor, der über die Tabelle iteriert und für jede Zeile das benötigte Stream-Objekt anlegt, den Binärstream hineinkopiert und es als Datei speichert:

DECLARE @Img VARBINARY(MAX)
    , @FileName VABINARY(MAX)
    , @ObjectToken INT

DECLARE IMGCURSER CURSOR FAST_FORWARD FOR 
    SELECT [Image], CAST('c:/folder/' + [Name] + '.img' AS varbinary(max)) AS Filename
    FROM   People
    WHERE  [Image] IS NOT NULL

OPEN IMGPATH 

FETCH NEXT FROM IMGCURSER INTO @Img, @FileName

WHILE @@FETCH_STATUS = 0 BEGIN

    EXEC sp_OACreate 'ADODB.Stream', @ObjectToken OUTPUT
    EXEC sp_OASetProperty @ObjectToken, 'Type', 1
    EXEC sp_OAMethod @ObjectToken, 'Open'
    EXEC sp_OAMethod @ObjectToken, 'Write', NULL, @Img
    EXEC sp_OAMethod @ObjectToken, 'SaveToFile', NULL, @FileName, 2
    EXEC sp_OAMethod @ObjectToken, 'Close'
    EXEC sp_OADestroy @ObjectToken

    FETCH NEXT FROM IMGCURSER INTO @Img, @FileName

END 

CLOSE IMGCURSER
DEALLOCATE IMGCURSER

Das Script, wie es bisher vorgestellt wurde, erzeugt Dateien mit der Endung “img”. Diese kann man zwar verwenden, aber natürlich wäre es besser gebräuchlichere Endungen zu benutzen, die auch dem Bildformat entsprechen. Dazu muss man das Select-Statement für den Cursor anpassen:

SELECT [Image], 
  CAST('c:\folder\' + [Name] + 
    (CASE WHEN substring([Image], 0, 3) = 0xFFD8 
      OR substring([Image], 0, 3) = 0xD0CF THEN '.jpg' 
      WHEN substring([Image], 0, 3) = 0x424D THEN '.bmp' 
      WHEN substring([Image], 0, 3) = 0x8950 THEN '.png' 
      WHEN substring([Image], 0, 3) = 0x4749 THEN '.gif' 
      ELSE '.img' END) AS varbinary(max)) AS Filename
FROM People
WHERE [Image] IS NOT NULL

Hierbei wird sich zunutze gemacht, dass die betrachteten Bildformate am Anfang jeweils ein spezifisches Bitmuster aufweisen. Wenn trotz diesem Konstrukt noch Dateien mit der Endung “img” erzeugt werden, muss die Fallunterscheidung passend ergänzt werden. In meinem Fall reichte diese Lösung aber für etwa 15000 Bilder aus.

Aufräumen

Um das Script ausführen zu können, mussten Einstellungen geändert werden. Sicherheitshalber sollte man diese Einstellungen auch wieder zurücksetzen:

sp_configure 'Ole Automation Procedures', 0;
GO
RECONFIGURE;
GO
sp_configure 'show advanced options', 0;
GO
RECONFIGURE;
GO