Question 127

Comment stocker une image dans une base de données ?

Avant toute chose, il faut savoir qu'il est totalement déconseillé de stocker des images dans une base de données. C'est très lourd, absolument inutile et cela peut même ralentir le serveur de données. Il ne faut pas perdre de vue que le but premier d'une base de données est de stocker des données à des fins de recherches, d'indexages ou de calculs. Ce qui n'a absolument aucune application dans le cas d'images. En bref, mieux vaut toujours stocker à la place le chemin (relatif, absolu, distant, ...) de l'image.

Néanmoins, si vous désirez quand même stocker une image dans une base de données, nous allons passer en revue les différentes techniques.

Tout d'abord, prenons le cas particulier d'Access qui permet, par le biais d'un champ de type OLE, de stocker des images. Cette solution, très pratique au sein même d'Access, l'est nettement moins lorsqu'il s'agit de récupérer les images dans Visual Basic. En effet, il vous sera impossible de lier ce champ à un contrôle "Image" ou "PictureBox". Tout simplement car on n'a plus affaire à une image, mais bien à un objet OLE. Remarquez au passage qu'il ne faut pas s'étonner si la taille de l'image stockée est supérieure à la taille de l'image d'origine, l'interface OLE rajoutant toute une série d'en-têtes (cfr. 123151 - ACC : Pourquoi les objets OLE font augmenter la taille des bases de données). Comment faire donc pour récupérer les images encapsulées dans un objet OLE. Deux solutions. La première est d'employer le contrôle "OLE" (livré en standard avec VB). Malheureusement, celui-ci ne peut être lié qu'à une source de données utilisant la technologie DAO. Si vous employer ADO, c'est râpé. La deuxième solution consiste à extraire, de l'objet OLE, l'image encapsulée. Pour ce faire, vous pouvez vous baser sur l'exemple décrit dans la fiche suivante : 119395 - How to Extract the Metafile from an OLE Control.

Vous l'aurez donc compris, OLE n'est pas vraiment la solution pour stocker des images. L'idéal est en fait de stocker les images sous forme binaire. Et cela vaut non seulement pour Access, mais aussi pour tout les autres types de base de données (Oracle, SQL Server, ...). Pour cela il faudra employer les méthodes AppendChunk et GetChunk qui permettent de lire et écrire des données binaires dans un champ de type binaire (équivalent au type "OLE" pour Access et "blob" pour SQL Server). Vous trouverez plusieurs exemples sur la MSDN et le site du support :

Enfin, si vous utiliser une version d'ADO supérieure ou égale à 2.5, vous pouvez employer l'objet Stream qui facilite grandement les opérations de lectures et écritures sur des champs binaires. Ce code permet, par exemple, d'écrire le contenu d'un fichier dans un champ binaire :

Option Explicit

Private Sub Form_Load()

    Dim rs As ADODB.Recordset
    Dim stm As ADODB.Stream

    Set rs = New ADODB.Recordset
    Set stm = New ADODB.Stream

    rs.Open "Table1", "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Mes Documents\MyDataBase.mdb", adOpenStatic, adLockOptimistic, adCmdTable

    stm.Type = adTypeBinary
    stm.Open
    stm.LoadFromFile "C:\Mes Documents\Photo.bmp"

    rs.AddNew
    rs!Nom = "Pierre Alexis"
    rs!Photo = stm.Read
    rs.Update

    rs.Close
    stm.Close
    Set rs = Nothing
    Set stm = Nothing

End Sub

Ensuite, pour récupérer cette image et l'afficher dans Visual Basic, le plus simple consiste à lier un contrôle Image à ce champ (en passant par un contrôle ADODC ou bien en liant un recordset par le code).

Si vous désirez récupérer l'image "manuellement" sans passer par une liaison de champ, la solution la plus simple (mais pas la plus performante), consiste à passer par un fichier temporaire et à employer ensuite la fonction LoadPicture de VB :

Option Explicit

Private Declare Function GetTempFileName Lib "kernel32" Alias "GetTempFileNameA" (ByVal lpszPath As String, ByVal lpPrefixString As String, ByVal wUnique As Long, ByVal lpTempFileName As String) As Long
Private Declare Function GetTempPath Lib "kernel32" Alias "GetTempPathA" (ByVal nBufferLength As Long, ByVal lpBuffer As String) As Long

Private Const MAX_PATH = 260

Private Sub Form_Load()

    Dim rs As ADODB.Recordset
    Dim stm As ADODB.Stream
    Dim sTemporyFileName As String

    Set rs = New ADODB.Recordset
    Set stm = New ADODB.Stream

    rs.Open "SELECT Photo FROM Table1 WHERE Nom = 'Pierre Alexis'", "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Mes Documents\MyDataBase.mdb", adOpenStatic, adLockOptimistic, adCmdText

    stm.Type = adTypeBinary
    stm.Open
    stm.Write rs.Fields("Photo").Value
    sTemporyFileName = GenerateTemporyFileName("PGM")
    stm.SaveToFile sTemporyFileName, adSaveCreateOverWrite And adSaveCreateNotExist
    Set Image1.Picture = LoadPicture(sTemporyFileName)
    Kill sTemporyFileName

    rs.Close
    stm.Close
    Set rs = Nothing
    Set stm = Nothing

End Sub

' Cette fonction permet de récupérer le chemin du dossier temporaire de Windows
' et d'ensuite générer un nom de fichier valide (càd qui n'existe pas encore)
' Le paramètre Prefix est une chaîne dont les trois premières lettres seront
' reprises comme initiales du fichier temporaire.
Public Function GenerateTemporyFileName(Optional Prefix As String = "TMP") As String

    Dim sBuffer As String
    Dim sTempFolderPath As String

    ' Initialisation des buffers
    sTempFolderPath = String$(MAX_PATH, Chr$(0))
    sBuffer = String$(MAX_PATH - 14, Chr$(0))

    If GetTempPath(MAX_PATH, sTempFolderPath) Then
        If GetTempFileName(sTempFolderPath, Prefix, 0&, sBuffer) Then
            GenerateTemporyFileName = Left$(sBuffer, InStr(1, sBuffer, Chr$(0)) - 1)
        End If
    End If

End Function

Concernant l'utilisation de l'objet Stream, vous pouvez aussi lire cette fiche : 258038 - HOWTO: Access and Modify SQL Server BLOB Data by Using the ADO Stream Object.

Date de publication : 13 février 2003
Dernière modification : 13 février 2003
Rubriques : Bases de données
Mots-clés : base de données, stocher, images, OLE, stream, ADO, ADOX, binary, binaire, picture, GetChunk, AppendChunk