SQlite et champs BLOB

Vous débutez et vous avez besoin d'aide ? N'hésitez pas à poser vos questions
Avatar de l’utilisateur
falsam
Messages : 7244
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

SQlite et champs BLOB

Message par falsam »

Dans une base de données SQL ont peut stocker des informations de type TEXT ou INTEGER etc ..... mais aussi de type BLOB

Cet type de champ permet de stocker par exemple des images et çà sera le thème de ce sujet.

Prérequis : Connaissance de PureBasic (of course) ainsi que de SQLite ou autres bases de données de type SQL.

Avant de commencer je vous propose cette courte vidéo pour vous montrer le résultat final.
:arrow: https://www.youtube.com/watch?v=eewRoK9W2sc

Pour commencer nous allons créer une base de données très simple permettant de stocker des images.

Cette base de données contiendra la table media ayant cette structure
  - IdMedia de tpe index auto incrémenté (INTEGER PRIMARY KEY)
  - Image de type BLOB
  - Filename de type TEXT

Créer un dossier et enregistrer le code de création de la base de données. Exemple : Medias - Create Database.pb

Code : Tout sélectionner

EnableExplicit

Global Database.i
Global DatabaseName.s = "assets.sqlite", ReqSql.s

;Création d'un fichier vierge
If CreateFile(Database, DatabaseName)
  CloseFile(Database) 
Else
  MessageRequester("Erreur", "Impossible de créer un fichier") 
  End
EndIf

;Ouverture et création de la base de données 
UseSQLiteDatabase()
If OpenDatabase(Database, DatabaseName, "", "", #PB_Database_SQLite)
  
  ;Préparation de la requête de création de la table des medias
  ReqSql = "CREATE TABLE medias (IdMedia INTEGER PRIMARY KEY, Image BLOB, FileName TEXT)"
    
  ;Exécution de la requete
  DatabaseUpdate(Database, ReqSql)  
  CloseDatabase(Database)
  
  If DatabaseError() = ""
    MessageRequester("Information","Création de la base terminée")
  Else
    MessageRequester("Information","Erreur lors de la création de la table 'medias'" + #CRLF$ + DatabaseError())
  EndIf
Else
  MessageRequester("Erreur","Erreur lors de la création de la base de données" + Chr(13) + DatabaseError() )
EndIf
C'est terminée avec la création de la base de données.

Vous pouvez utiliser SQLite Explorer pour voir la structure de la base.

Passons maintenant à l'exploitation des images ........
Configuration : Windows 11 Famille 64-bit - PB 6.03 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Avatar de l’utilisateur
falsam
Messages : 7244
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: SQlite et champs BLOB

Message par falsam »

Expoitation des images.

Enregistrer ce code dans le dossier contenant la base de données que nous venons de créer. Exemple: Medias.pb

Ce code permet
  -Sélectionner une image et l'enregistrer dans la base de données.
  -Afficher chacune des images dans une fenêtre.
  -Supprimer une image.

La base de données est ouverte qu'une seule fois lors de l’exécution et fermer quand on quitte l'application.

Code : Tout sélectionner

; PureBasic 5.70 LTS (x64)

EnableExplicit

Enumeration 
  #DataBase
  #ImageFile
  #Buffer
EndEnumeration

Enumeration Window
  #MainForm
EndEnumeration

Enumeration Gadget
  #Selector
EndEnumeration

;Plan du code
Declare Start()                           ;Début
Declare ShowMedia(LastSequence = #False)  ;Affichage d'une ou des images 
Declare OnSelectMedia()                   ;Sélection d'une image
Declare OnDeleteMedia()                   ;Suppression d'une image
Declare OnClose()                         ;Fin

Start()

Procedure Start()
  UseJPEGImageDecoder()
  UsePNGImageDecoder()
  
  UseSQLiteDatabase()
  OpenDatabase(#DataBase, "assets.sqlite", "","", #PB_Database_SQLite)
  
  OpenWindow(#MainForm, 0, 0, 800, 600, "SQLite et les champs de type BLOB", #PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_SizeGadget)      
  ButtonGadget(#Selector, 10, 10, 180, 32, "Sélectionner une image")
  
  ShowMedia() ;Affichage de toutes les images
  
  ;Evenements
  BindEvent(#PB_Event_CloseWindow, @OnClose())
  BindGadgetEvent(#Selector, @OnSelectMedia())
  
  Repeat : WaitWindowEvent(10) : ForEver
EndProcedure

;Affichage de la derniere image ou toutes les images 
Procedure ShowMedia(LastSequence = #False)
  Protected Window, IdMedia, ImageEncode.s, FileName.s, *Buffer, Result, Gadget
  
  If LastSequence = #True
    ;Selection de la derniere image
    DatabaseQuery(#DataBase, "SELECT IdMedia, image, filename FROM Medias ORDER BY IdMedia DESC LIMIT 1")
  Else
    ;Selection de toutes les images 
    DatabaseQuery(#DataBase, "SELECT IdMedia, image, filename FROM Medias")
  EndIf
  
  ;Affichage du resultat : Chaque image est affichée dans une fenetre
  While NextDatabaseRow(#Database)
    IdMedia     = GetDatabaseLong(#Database, 0)
    ImageEncode = GetDatabaseString(#Database, 1)
    FileName    = GetDatabaseString(#DataBase, 2)
       
    *Buffer = AllocateMemory(Len(ImageEncode)*1.35)
    Result = Base64Decoder(ImageEncode, *Buffer, MemorySize(*Buffer))
    
    If Result
      ReAllocateMemory(*Buffer, Result)
      CatchImage(#Buffer, *Buffer)
      Window = OpenWindow(#PB_Any, 0, 0, ImageWidth(#Buffer), ImageHeight(#Buffer) + 50, FileName, #PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_TitleBar)
      ImageGadget(#PB_Any, 0, 0, 0, 0, ImageID(#Buffer))
      
      ;Ajout du bouton de suppression 
      ;Mémorisatio de l'index d'enregistrement
      ;Initialisation du Callback de suppression
      Gadget = ButtonGadget(#PB_Any, 10, ImageHeight(#Buffer) + 10, 80, 22, "Supprimer")
      SetGadgetData(Gadget, IdMedia)
      BindGadgetEvent(Gadget, @OnDeleteMedia())
                      
      StickyWindow(Window, #True)
    EndIf
  Wend  
EndProcedure

;Selection d'une image
Procedure OnSelectMedia()
  Protected Pattern.s = "Image Files (*.bmp, *.jpg, *.png)|*.bmp;*.jpg;*.png"
  Protected FileName.s = OpenFileRequester("Sélectionner une image", "", Pattern, 0)
  Protected ImageEncode.s, *Buffer, Result.s
  
  If FileName <> ""  
    ;Lecture et encodage de l'image
    If ReadFile(#ImageFile, FileName)
      *Buffer = AllocateMemory(Lof(#ImageFile))
      ReadData(#ImageFile, *Buffer, Lof(#ImageFile))
      CloseFile(#ImageFile)
      
      ImageEncode = Space(MemorySize(*Buffer)*1.35)
      Result = Base64Encoder(*Buffer, MemorySize(*Buffer))      
      ImageEncode = RTrim(Result)
    EndIf
    
    ;Enregistrement de l'image dans la base de données 
    DatabaseUpdate(#DataBase, "INSERT INTO medias (Image, FileName) VALUES ('" + ImageEncode + "', '" + GetFilePart(FileName) + "')")
    
    ;Affichage de la DERNIERE image insérée
    ShowMedia(#True)
  EndIf
EndProcedure

;Suppression d'une image
Procedure OnDeleteMedia()
  Protected IdMedia = GetGadgetData(EventGadget())
  
  DatabaseUpdate(#DataBase, "DELETE FROM Medias WHERE IdMedia=" + IdMedia)
    
  If DatabaseError() <> ""
    MessageRequester("Information", "Erreur lors de la suppression" + #CRLF$ + DatabaseError())
  Else
    CloseWindow(EventWindow())
  EndIf
EndProcedure

Procedure OnClose()
  Protected Window = EventWindow()
  Select Window
    Case #MainForm
      CloseDatabase(#DataBase)
      End
    Default
      CloseWindow(Window)
  EndSelect
EndProcedure
Vous pouvez utiliser SQLite Explorer pour voir la structure et les données de la base.
Configuration : Windows 11 Famille 64-bit - PB 6.03 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Avatar de l’utilisateur
falsam
Messages : 7244
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: SQlite et champs BLOB

Message par falsam »

Modification du code d'exploitation des images : Il est possible de supprimer une image de la base de données.
Configuration : Windows 11 Famille 64-bit - PB 6.03 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Avatar de l’utilisateur
falsam
Messages : 7244
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: SQlite et champs BLOB

Message par falsam »

Hello. Modification du code d'exploitation des images pour être en conformité avec la version 5.70 de PureBasic.

Ce code a été testé dans un environnement Windows 10 x64 et Mac OS x64.
Configuration : Windows 11 Famille 64-bit - PB 6.03 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Avatar de l’utilisateur
Fig
Messages : 1176
Inscription : jeu. 14/oct./2004 19:48

Re: SQlite et champs BLOB

Message par Fig »

Je me demandais pourquoi je n'avais pas eu la notification youtube... Et puis j'ai vu la date du sujet, tout s'explique. ^^
Merci pour cette mise à jour en tout cas. :D
Il y a deux méthodes pour écrire des programmes sans erreurs. Mais il n’y a que la troisième qui marche.
Version de PB : 6.00LTS - 64 bits
Avatar de l’utilisateur
falsam
Messages : 7244
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: SQlite et champs BLOB

Message par falsam »

Le sujet date un peu et avait besoin d'une bonne mise à jour. Ce qui me désole c'est de ne pas pouvoir lire les bases de données précédentes une fois le code mise à jour.

Suivant les versions de PureBasic, Les fonctions Base64Encoder() et Base64Decoder() ne fonctionnent pas de la même manière. Compiler avec PureBasic 5.70, mon ancienne base de données contenant des images n'est plus exploitable !
Configuration : Windows 11 Famille 64-bit - PB 6.03 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: SQlite et champs BLOB

Message par Ollivier »

Ça donne quoi si tu dématérialises le hachage ?
(pour test j'entends :mrgreen: )
Avatar de l’utilisateur
falsam
Messages : 7244
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: SQlite et champs BLOB

Message par falsam »

Ollivier a écrit :Ça donne quoi si tu dématérialises le hachage ?
(pour test j'entends :mrgreen: )
Je te laisse le soin d'essayer et de nous faire part de ton expérimentation !
Configuration : Windows 11 Famille 64-bit - PB 6.03 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: SQlite et champs BLOB

Message par Ollivier »

"Petit Prince", je ne veux pas te piquer, mais ne me demande pas de faire un sprint pour t'aider. Ça sert à rien : c'est toi qui a les jambes.

Au vu de la simplicité de cette fonction, je suppose que c'est le goulot pour permettre une compatibilité infinie des outils de traitement, pas la sécurité.

Je me trompe?
Avatar de l’utilisateur
falsam
Messages : 7244
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: SQlite et champs BLOB

Message par falsam »

Ollivier a écrit :"Petit Prince", je ne veux pas te piquer, mais ne me demande pas de faire un sprint pour t'aider. Ça sert à rien
Ca sert à rien ? Evite de me poser des questions dans ce cas quand tu peux obtenir des réponses par toi même !
Configuration : Windows 11 Famille 64-bit - PB 6.03 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Mouillard
Messages : 77
Inscription : mer. 13/sept./2017 14:35
Localisation : Picardie (Somme)

Re: SQlite et champs BLOB

Message par Mouillard »

Bonsoir à tous,
:arrow: Si Si ça sert à quelque chose ....Bravo Falsam très bonne :idea:
Et j'ajoute de très bons codes ""Mériteraient" un recyclage de ce genre, sinon ces codes seront perdus à jamais... :wink:
Car derrière il y a l'idée... POUR AVANCER encore et encore :!: Merci Falsam :)
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: SQlite et champs BLOB

Message par Ollivier »

Ne t'égare pas Falsam. Et ne m'invente pas je ne sais quel contexte.

Je pose les questions AVANT que ton travail de mise à jour soit fait. Parce qu'on ne va sûrement pas s'amuser en ASM, APRES avec un gagnant qui crie victoire juste pour la gratuité de l'amusement et toi qui doit te REtaper une autre mise à jour (Si j'ai bien saisi ta problématique).
Marc56
Messages : 2146
Inscription : sam. 08/févr./2014 15:19

Re: SQlite et champs BLOB

Message par Marc56 »

Je n'ai pas lu tout le code, mais il n'est plus nécessaire d'encoder les images (Base64Encoder) depuis la v5.40
Le type de données Blob, c'est du binaire, on peut y mettre n'importe quoi directement.
L'encodage64 c'est pour transformer du binaire en texte quand on est sur un système 7 bits.

À partir de la version 5.40 LTS (16 octobre 2015) on a SetDatabaseBlob() qui fera le job tout seul.

D'où effectivement l'intérêt de "moderniser" les tutos

Voir les posts de srod
https://www.purebasic.fr/english/viewto ... 12#p498212

:wink:
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: SQlite et champs BLOB

Message par Ollivier »

Merci Marc56 pour ton aide inlassable et grâcieuse.
Marc56
Messages : 2146
Inscription : sam. 08/févr./2014 15:19

Re: SQlite et champs BLOB

Message par Marc56 »

La procédure OnSelectMedia() peut donc être simplifiée si on a PB >= 5.40 LTS

Code : Tout sélectionner

;Selection d'une image
Procedure OnSelectMedia()
  Protected Pattern.s = "Image Files (*.bmp, *.jpg, *.png)|*.bmp;*.jpg;*.png"
  Protected FileName.s = OpenFileRequester("Sélectionner une image", "", Pattern, 0)
  Protected *Buffer
  
  If FileName <> ""  
    ;Lecture et encodage de l'image
    If ReadFile(#ImageFile, FileName)
      *Buffer = AllocateMemory(Lof(#ImageFile))
      ReadData(#ImageFile, *Buffer, Lof(#ImageFile))
      CloseFile(#ImageFile)

      SetDatabaseBlob   (#DataBase, 0, *Buffer, MemorySize(*Buffer))
      SetDatabaseString (#DataBase, 1, GetFilePart(FileName))
    EndIf
    DatabaseUpdate(#DataBase, "INSERT INTO medias (Image, FileName) VALUES (?, ?)")
    
    ;Affichage de la DERNIERE image insérée
    ;ShowMedia(#True)
  EndIf
EndProcedure
On peut vérifier que l'image est bien stockée dans la base avec par exemple SQLiteStudio qui affiche le champs image (double-clic que le champ image, puis onglet 3)

:arrow: Reste à modifier ShowMedia() pour que l'exemple soit complet.

C'est quand même bien ces variables de liaison :) (requêtes préparées / Prepared statement)

:wink:
Répondre