Grails – Ecrire et lire un champ Blob

Présentation rapide de Grails:

Grails est un framework, de la communauté SpringSource, qui permet de produire des applications web très rapidement.
Il s'appuie sur des frameworks tel que : Spring, Spring MVC et Hibernate.
Le développement se fait avec le langage Groovy.
Pour plus d'informations : site officiel de grails

Problématique:

Voici un article pour ceux qui ont eu des problèmes pour écrire ou lire un champ blob avec Grails ou plutôt GORM (Grails Object Relational Mapping).
GORM gère très bien les CRUD sur des champs de type courants (chaine de caractères, date, nombre, ...), par contre lorsqu'il s'agit d'un type BLOB (Binary Large OBject), les choses se compliquent :'(
Je vais vous présenter la méthode que j'ai utilisé avec une base Oracle 9i.

Solution:

Le principe est assez simple : il faut créer un champ 'transient' (champ non persisté) pour contenir le champ BLOB sous forme de tableau de 'bytes'.
C'est facile à dire mais avec un morceau de code, c'est mieux ^_^

Voici une classe qui représente ma table en base de données:
Extrait du fichier : Files.groovy

import java.io.BufferedOutputStream;
import java.sql.Blob
import java.sql.SQLException
import org.hibernate.Hibernate

class Files implements java.io.Serializable{
    static mapping = {
         table 'FILES'
         version false
         id column:'ID_FICHIER', generator:'assigned'
         columns {
         	dCreation type:'date'
         	fichier type:'blob'  // force le type BLOB en base
      	 }

    }

    static transients = ["tempFile"] // champ non sauvegardé en base
    Long id
    String nomFichier
    String extension
    byte[] tempFile	// sert a convertir en blob
    java.sql.Blob fichier
    String cUtCreation
    Date dCreation

    static constraints = {
	id(size: 0..10)
        nomFichier(size: 1..150, blank: false)
        extension(size: 1..10, blank: false)
        fichier(maxSize:10485760, blank: false) // taille max 10Mo
        cUtCreation(size: 1..50, blank: false)
        dCreation()
    }

	def getTempFile() {
		if (fichier == null)
			return null;
		return toByteArray(getFichier());
	}

	def setTempFile(tableau) {
		setFichier(Hibernate.createBlob(tableau.getBytes()));
	}

	boolean equals(obj) {
		if (this == obj) return true;
		if (!obj || obj.class != this.class) return false;
		return id?.equals(id)
	}

	int hashCode() {
		return id ? id.hashCode() : super.hashCode()
	}

	private byte[] toByteArray(Blob blobField) {
		def baos = new ByteArrayOutputStream()
		try {
			def debut = System.currentTimeMillis()
                        // copie l'InputStream dans l'OutputStream de façon optimisé
                        // ça parait magique mais ça marche ;)
			baos << blobField.getBinaryStream()
			def fin = System.currentTimeMillis() - debut
			println "temps de traitements : ${fin} ms"
			return baos.toByteArray()
		} catch (SQLException e) {
			throw new RuntimeException(e);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

    String toString() {
        return "${id}"
    }
}

Explication:

Pour affecter les valeurs du champ 'fichier', qui représente le BLOB, on utilise les accesseurs(getter, setter) du champ 'tempFile'.
Celui-ci se charge de convertir le BLOB en tableau d'octets (byte[]) et vice-versa.

Attention, certaines méthodes sont appelés dans le code ci-dessus mais n'apparaissent pas dans le code. Ceci est tout à fait normal car elle sont généré automatiquement par Groovy.
Par exemple : getFichier() --> getter du champ 'fichier'

Création de l'objet BLOB:

Pour créer le BLOB, on utilise la méthode : Hibernate.createBlob(byte[])
Dans la méthode setTempFile(tableau), tableau est de type String (chaine de caractères).

Lecture de l'objet BLOB :

Pour lire le champ BLOB, on utilise la méthode : toByteArray(Blob blobField)
Ici on récupère le champ 'fichier' en appelant : blobField.getBinaryStream()
Cette méthode renvoie un ByteArrayInputStream.
Il ne reste plus qu'à la récupérer et la convertir en tableau d'octets (byte[]).

Point d'attention :

Si vous voulez stocker de gros fichier dans les champs BLOB dans une base Oracle 9i, il faut savoir que le driver JDBC Oracle 9i est buggué :'(
La solution consiste à utiliser un driver JDBC 10g.
Avec le driver 9i, j'avais des erreurs quand je sauvegardais des fichiers d'environ 100ko (donc très petits).
Avec le client 10g, je peux stocker des fichiers de plus de 20Mo (voire plus car non testé).
Si vous voulez tester, il faudra enlever la contrainte de taille maximum sur le champ 'fichier' (que j'ai ajouté plus tard pour limiter les abus ^_^ )
fichier(maxSize:10485760, blank: false) // taille max 10Mo
Il suffit de supprimer l'attribut 'maxSize'.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.