Persistencia de imágenes con Python: SQLServer y cPickle

Hola. En este artículo vamos a ver cómo guardar y recuperar imágenes de una base de datos Microsoft SQLServer 2005 Express Edition, con Python, así como guardar y recuperar también, imágenes, en un objeto que se serializa, guardando dicha información en disco, mediante cPickle.

Antes de nada comentar que en este blog se han tratado varios tipos de SGBD, tales como MySQL, Firebird,  ó SQLServer 2008. El elegir SQLServer 2005 Express Edition es simplemente para probar que Python funciona, mediante pyodbc, con la mayoría de los sistemas gestores de bases de datos actuales. Y para ver si funciona lo mejor es probarlo uno mismo, ya que no es lo mismo conocer el camino que andar el camino.

NOTA: Este post lo he escrito en la plataforma Microsoft Windows XP SP3, con Python 2.6.6.

Imágenes en SQLServer 2005 con Python

Lo primero de todo es descargar el SGBD Microsoft SQLServer 2005 Express Edition.


Se puede hacer desde aquí:

http://www.microsoft.com/downloads/details.aspx?FamilyID=220549b5-0b07-4448-8848-dcc397514b41&displayLang=es

También (recomendable) es posible descargarse el Microsoft SQL Server Management Studio Express, el cual es un entorno gráfico para gestionar las bases de datos que vayamos creando, de la siguiente URL:

http://www.microsoft.com/downloads/details.aspx?displaylang=es&FamilyID=c243a5ae-4bd1-4e3d-94b8-5a0f62bf7796#filelist

Instalamos los dos paquetes de software....


Una vez instalado, nos vamos al Management Studio y creamos una base de datos, llamada imagenes, con una tabla, llamada registro.


Como podemos observar, se crea una tabla con tres campos, un int, un nvarchar y un tipo image.

Una vez tenemos instalado SQLServer 2005, con nuestra base de datos y nuestra tabla, vamos a instalar pyodbc, el software de Python para crear conexiones ODBC a orígenes de datos, en este caso, el software de Microsoft. Para ello, descargamos el pyodbc, desde http://code.google.com/p/pyodbc/. Creamos a continuación, mediante las Herramientas Administrativas/Orígenes de datos (ODBC), nuestra conexión:


NOTA: El lector debe de tener en cuenta que existe un módulo específico para la conexión de SQLServer con Python, llamado pymssql, que se puede obtener de http://code.google.com/p/pymssql/.

NOTA 2: Pudiera ser que con SQLServer 2005 Express Edition aparezcan ciertos errores a la hora de crear la conexión ODBC referidos a la configuración del mismo, con el propósito de permitir conexiones remotas. Bien, la solución la tenéis aquí: http://support.microsoft.com/kb/914277/es.

Una vez creado el ODBC y la conexión correctamente realizada, ya tenemos todo listo para programar en Python.

El código para crear una conexión desde Python a SQLServer 2005, mediante pyodbc, podría ser tal que así:


import pyodbc


# Cadena de conexión.
cadenaConexion = 'DSN=imagenes;UID=ABUSIMBEL\familia'


try:
    conexionSQLServer = pyodbc.connect(cadenaConexion)
    cursorSQLServer = conexionSQLServer.cursor()
    print "Conectado con SQLServer 2005 Express!"
except:
    print "No he podido conectar con base de datos SQLServer!"

# Cerramos cursor y conexión.
print "Cerrando conexiones!"
cursorSQLServer.close()
conexionSQLServer.close()

Si todo ha ido bien, debe de aparecer algo como esto:

Conectado con SQLServer 2005 Express!
Cerrando conexiones!

A continuación vamos a insertar una imagen en la tabla registro de la base de datos imagenes. El código podría ser tal que así:

import os

fichero_imagen = os.path.realpath('alvaro.jpg')
imagen = open(fichero_imagen,'rb').read()

Cargamos un fichero jpg, y lo referenciamos mediante imagen. Se supone que el fichero ha de existir. El código para insertar una imagen en la base de datos es el siguiente:

# Cadena SQL a ejecutar.
cadenaSQL = "insert into registro values (1,'alvaro',?)"
# Lanzamos Sentencia.
cursorSQLServer.execute(cadenaSQL, (pyodbc.Binary(imagen),))
# Confirmamos escritura.
conexionSQLServer.commit()

Cabe destacar que transformamos en binario la imagen cargada del fichero. Además hacemos commit, no olvidarlo. Si nos vamos al Management Studio y hacemos una consulta a la tabla tenemos la solución:


Bien, ya sabemos como insertar imágenes en la base de datos. Ahora toca como recuperar la información, mediante el select correspondiente. El código para hacer esto podría parecerse a lo siguiente:

# Creamos el nombre de un fichero destino.
fichero_destino = os.path.realpath('destino.jpg')
# Lanzamos la consulta de selección SQL.
imagen = cursorSQLServer.execute('SELECT * FROM registro WHERE id = 1')
# Creamos un fichero de destino (temporal).
fichero_final = open(fichero_destino,'wb')
# Escribimos en el fichero y lo cerramos.
fichero_final.write(imagen.next()[2])
fichero_final.close()

Cabe destacar que se crea un fichero de salida temporal (destino.jpg) para obtener en disco el fichero seleccionado de la base de datos. También decir que utilizamos next() para iterar sobre el cursor y obtener la imagen, que se encuentra en la posición 3 de la tupla (es decir, imagen.next()[2]), ya que la tabla tiene 3 campos y el campo imagen es el tercero.

Decir que si se quiere que se vea automáticamente la imagen recuperada, se puede utilizar:

fichero_destino = os.path.realpath('destino.jpg')
os.startfile(fichero_destino)

En mi sistema Windows, automáticamente se abre el programa asociado a ficheros de imágenes, Visor de imágenes y faz de Windows, dando como resultado:


Imágenes serializadas en objetos con Python

Bien. Estamos en la segunda parte del post. Ahora lo que vamos a hacer es guardar una imagen dentro de un objeto y dicho objeto guardarlo en disco. Sería así como un wrapper de la imagen que se guardará en disco. Lo bueno que tiene esto es que se pueden guardar múltiples imágenes en cualquier tipo de contenedor (lista, tupla, diccionario) y guardarla directamente en disco, a capón. Para ello vamos a crear primero una clase, llamada persistencia, que tendrá la funcionalidad de cargar y guardar un objeto.

El código que define la clase persistencia es el siguiente:


import cPickle


class persistencia(object):
    def nombre_clase(self):
        return str(self).split(' ')[0].split('.')[1]


    def cargar(self, nombre_fichero = None):
        if nombre_fichero is None:
            nombre_fichero = self.nombre_clase()
        manejador_fichero = open(nombre_fichero,'r')
        objeto = cPickle.load(manejador_fichero)
        manejador_fichero.close()
        return objeto


    def salvar(self, objeto, nombre_fichero = None):
        if nombre_fichero is None:
            nombre_fichero = self.nombre_clase()
        # Fichero que contendrá el objeto.
        manejador_fichero = open(nombre_fichero,'w')
        # Volcamos el objeto de memoria al fichero.
        cPickle.dump(self, manejador_fichero)
        # Cerramos fichero.
        manejador_fichero.close()


Ahora, únicamente hay que crear una clase que herede de persistencia, para obtener su funcionalidad, e implementar las estructuras necesarias para guardar imágenes. Para ello implementamos la clase registro, tal que así:


class registro(persistencia):
    def __init__(self):
        self._registro = []


    def insertar_persona(self, nombre, foto):
        self._registro.append([nombre, foto])


    def devolver_persona(self, nombre = None):
        if nombre is None:
            return self._registro
        else:
            for i in self._registro:
                if i[0] == nombre:
                    return i
        return None

Ahora falta, por fin, utilizar las clases que hemos diseñado, para poder ver que efectivamente funciona la idea. El código para serializar una imagen en un objeto podría tener el siguiente:

import os

# Cargamos imagen.
fichero_imagen = os.path.realpath('alvaro.jpg')
imagen = open(fichero_imagen,'rb').read()

# Instanciamos la clase que contendrá el registro de personas.
personas = registro()
personas.insertar_persona('alvaro',imagen)
personas.salvar(personas)

Como podemos observar la funcionalidad heredada nos permite guardar la imagen en un fichero, mediante la serialización del objeto. Decir que hemos incluido una imagen únicamente. Podríamos incluir tantas imágenes como quisiéramos. Evidentemente se crea un fichero, llamado registro, el cual tiene el siguiente contenido:


El formato del fichero nos da igual. Lo importante es que podamos recuperar la información guardada en las estructuras de datos de alto nivel que dispone Python. Vamos por último a ver cómo recuperar la información guardada en el objeto serializado. Podríamos utilizar un código como este: 

import os

# Obtenemos datos.
personas = registro()
personas = personas.cargar()
individuo = personas.devolver_persona('alvaro')

# Creamos fichero.
fichero_destino = os.path.realpath('destino.jpg')
imagen = open(fichero_destino,'wb')
imagen.write(individuo[1])
imagen.close()

# Y lo mostramos por pantalla, para ver que efectivamente funciona la idea.
os.startfile(fichero_destino)

Esto es todo por el momento. Saludos.

Comentarios

Entradas populares de este blog

Ejercicios resueltos de programación, en Python y en español, I.

Herramientas (IDE, GUI, EDITOR) para desarrollar en Python

pyLorca: Diseño y diagrama de clases, en Python