Xojo es un entorno de desarrollo multiplataforma excelente para crear todo tipo de aplicaciones; y si se trata de aplicaciones que deban de trabajar con bases de datos, entonces encontraremos que podremos crear nuestras soluciones en un abrir y cerrar de ojos, ya sea usando SQLite o los motores de bases de datos cliente/servidor PostgreSQL, MySQL (MariaDB), Microsoft SQL Server, Oracle o cualquier otra a la que podamos acceder mediante un controlador compatible ODBC.
Ahora bien, quizá en tu proyecto contemples la posibilidad de guardar imágenes en una o más columnas de la base de datos y, en este caso, siempre suele aparecer la misma pregunta en los foros de Xojo: ¿cómo puede guardarse una imagen y recuperarla posteriormente? ¿Cuál es el mejor método para hacerlo?
Pues bien, esto es lo que veremos en esta entrada, aplicado sobre una base de datos SQLite, si bien lo explicado en este caso también es aplicable sobre cualquier otro de los motores de bases de datos soportados simplemente cambiando algunas de las clases utilizadas en este ejemplo para que se correspondan con la base de datos utilizada en su caso (por ejemplo MySQLPreparedStatment en vez de SQLitePreparedStatment).
Diseño de la base de datos
Si bien en esta entrada veremos como podemos guardar y recuperar imágenes hacia y desde una base de datos, es necesario preguntarse en primer lugar si es imperativo guardar la información propiamente dicha (los datos de la imagen) sobre la tabla de la base de datos o si simplemente es suficiente con que guardemos la ruta hacia la imagen en cuestión.
Hay que tener en cuenta que si guardamos un gran volumen de imágenes de gran tamaño en la base de datos esta incrementará su tamaño de forma notable, algo que puede llegar a suponer un problema en el caso de que vayamos a desplegar nuestras aplicaciones Xojo como ejecutables de 32 bits (dado que, en tal caso, el tamaño máximo soportado rondará los 4 GB). Igualmente, y en función del tamaño de las imágenes almacenadas, también podríamos penalizar las transaciones realizadas hacia y desde la base de datos en cuestión, sobretodo si vamos a utilizar motores de bases de datos remotas. Ahora bien, en el lado positivo, el almacenamiento de las imágenes nos garantizará el acceso a las mismas dado que sabremos que, efectivamente, están almacenadas en la base de datos.
Mi recomendación: procura usar el almacenamiento de imágenes en la base de datos cuando se trate de imágenes de un tamaño (o peso) pequeño, procurando que no excedan 1 MB.
La otra alternativa supone almacenar en la tabla de la base de datos únicamente las referencias sobre la ubicación (ruta) donde se encuentren almacenadas. Esto permite trabajar con bases de datos de menor tamaño y también agilizar las transferencias. En el aspecto “negativo”, sólo obtendremos los resultados esperados si las imágenes se van a encontrar en todo momento en la ubicación o ruta almacenada en la base de datos; de lo contrario, la app fallará o deberá de implementar otros mecanismos para recuperar la imagen “extraviada”.
En este tutorial veremos únicamente la primera de las opciones: guardar la imagen efectivamente en la base de datos. En este sentido, nos aseguraremos de definir en nuestra base de datos las columnas que vayan a contener los datos de las imágenes con el tipo BLOB (objeto de gran tamaño o datos en “crudo”). Esto es fundamental como veremos posteriormente en el uso de la instrucción SQL encargada de crear el nuevo registro empleando los datos de imágen (o actualizar el registro, según sea el caso).
De imagen… a texto
Antes de que podamos realizar la transacción de una imagen hacia la base de datos con la que estemos trabajando, es imprescindible convertir previamente la imagen en cuestión (una instancia de la clase Picture) a formato de texto, de modo que podamos almacenar sus contenidos por ejemplo en una variable de tipo String y, así, incorporarla como parte de la composición SQL en una instrucción Insert o Update.
Lo cierto es que Xojo nos lo pone realmente sencillo también en esto al proporcionarnos el método GetData que podemos invocar sobre cualquier instancia de tipo Picture.
Como puedes ver en la documentación, este método espera dos parámetros obligatorios. El primero de ellos es el formato en el que deseamos obtener los datos de la imagen; por ejemplo, JPEG, PNG, BMB, etc. El segundo de los parámetros indicará la calidad (nivel de compresión) que deseamos aplicar durante dicho proceso de conversión. Como resultado de la ejecución del método obtendremos una instancia de un MemoryBlock. Por ejemplo, si partimos de que la variabla “P” apunte a una instancia válida de Picture, el siguiente código:
Dim mb As MemoryBlock = p.getData(Picture.FormatJPEG, Picture.QualityHigh)
La variable mb contendrá los datos correspondientes a la imagen apuntada por la variable “p” con el formato apuntado durante la conversión.
Ahora bien, podemos sacar ventaja de la relación directa de asignación que existe entre los bloques de memoria y las string, de modo que sustituyendo la anterior sentencia por la siguiente:
Dim data as String = p.getData(Picture.FormatJPEG, Picture.QualityHigh)
Nos proporcionará en la variable “data” la cadena de texto correspondiente a los datos de la imagen contenida por la instancia de tipo Picture apuntada por la variable “p”. ¡Listo!
Trabajar con BLOB: ¡PreparedStatments al rescate!
Ya hemos visto en otras entradas la utilidad principal de los PreparedStatments a la hora de trabajar con los diferentes tipos de bases de datos soportados, dado que nos permiten evitar ataques de inyección SQL. Pero además también suponen la forma más directa de pasar los datos de nuestra imagen, almacenados en una variable de tipo String, ¡para que se guarden en efecto en la columna de tipo BLOB de la base de datos!
Pongamos por caso que la propiedad “db” apunta a una instancia válida de una base de datos SQLite. Así, mediante el siguiente fragmento de código insertaremos un nuevo registro en el que uno de sus campos es de tipo BLOB:
dim ps as SQLitePreparedStatement = app.db.Prepare("insert into imagenes(nombre,imagen) values(?,?)") ps.BindType(0, SQLitePreparedStatement.SQLITE_TEXT) ps.BindType(1, SQLitePreparedStatement.SQLITE_BLOB) ps.Bind(0,name) ps.Bind(1,data) ps.SQLExecute
Así, no sólo estaremos sanitizando nuestra entrada de datos hacia la base de datos (algo que hemos de acostumbrarnos a hacer en todos los casos), sino que también lograremos guardar los datos de la imagen correctamente.
Generar la imagen a partir de los datos BLOB
Ya hemos visto cómo guardar la imagen en la base de datos… ¿y para recuperarla? Seguramente lo habrás averiguado: ¡sólo hay que invertir el proceso!
De igual modo que la clase Picture cuenta con el método GetData, también nos ofrece el método FromData. En este caso se toma como único parámetro un bloque de memoria y nos devolverá como resultado una instancia válida (entendiendo que se pueda generar una imagen a partir de los datos contenidos en el bloque de memoria, claro). Obviamente, en este caso también podemos sustituir el bloque de memoria por una cadena, facilitando así aun más las cosas al obtener los datos a partir de la consulta realizada sobre la base de datos.
Por tanto, con la siguiente línea de código:
Dim p As Picture = Picture.FromData(rc.Field("imagen").StringValue)
Estaremos obteniendo en la variable “p” una instancia de imagen válida a partir de los datos almacenados en la columna “imagen” del cursor (RecordSet) obtenido a partir de una consulta SQL válida contra la base de datos. A partir de ahí, ya podremos utilizar la imagen sobre cualquier elemento de nuestra interfaz de usuario, como pueda ser un Canvas o un control ImageWell, por ejemplo.
Conclusiones
Como has podido comprobar, guardar y recuperar imágenes en una base de datos no es particularmente complejo gracias a las ayudas que, como siempre, nos procuran el propio Framework. Y, lo mejor de todo, es que no tendrás que modificar ni una línea de código para que funcione bajo cualquiera de los sitemas operativos de despliegue soportados.
Y si bien en este texto hemos visto los aspectos clave sobre el tema expuesto, te recomiendo que reproduzcas el vídeo que acompaña este tutorial para que veas su funcionamiento en la práctica sobre una aplicación de ejemplo.
Deseo saber si dispone de alguna aplicacion de ejemplo para firma digital XADES-BES pues necesito firmar archivo XML de factura electronica.
Aquiles
Hola Aquiles,
No dispongo de ejemplos tan específicos. La gran mayoría de entradas en este blog giran en torno al lenguaje, framework e IDE de Xojo.
No vendría mal poner el código del botón que carga la imagen en el ImageWell. Para los nuevos nos facilita mucho el trabajo.
Le doy las gracias por estos estupendos tutoriales.
Hola, no se si es el lugar indicado, pero tengo un problema con bases de datos, te pongo en antecedentes.
Hace mas de 20 que no programaba, ahora he cogido Xojo y estoy quedando muy contento,voy pillando el hilo, ahora tengo un problema, he creado una BD para una asociación de voluntarios, es sin animo de lucro y de paso hago practicas, resulta que en las tablas están introduciendo datos, pero ven que necesitan alguna columna mas es ciertas tablas, se que con Alter table puedo modificar la tabla, pero quisiera poder leer los campos que tiene la tabla, y si le falta algún campo (columna) añadirla, pero no lo consigo, lo he intentado con rs.idxfield, pero no logro lo que quiero.
Gracias de antemano, por cierto las dos publicaciones que tiene son geniales y me han ayudado mucho aun y así sigo teniendo dudas que si no le sabe mal iré consultando, aunque no se si en el blog es el lugar apropiado.
Gracias Juan Ramón por tus comentarios. ¡Me encanta que encuentres de utilidad el contenido del Blog!
Para solucionar tu problema, utiliza FieldSchema utilizando el nombre de la tabla sobre la cual desees obtener información.
Puedes acelerar considerablemente el aprendizaje de Xojo con los eBook que encontrarás en la tienda… y especialmente también con los bonos de Consultoría/formación. Créeme… ayudan a “abrir las puertas” y entender conceptos fundamentales sobre los cuales construir y avanzar más rápido.
Especialmente estos días, durante el BlackFriday, puedes aprovechar un 20% de descuento utilizando el cupón “Blackfriday2017”
Hola Javier, tienes un ejemplo básico igual pero sobre el MySQL porque ese mismo ejemplo me da error, quiero grabar una imagen y luego leerla desde VB6 para poder integrar un modulo del xojo. gracias!