Tratamiento de imagenes

Hola que tal, saludos a todos...

Miren tengo imagenes en una tabla de oracle con un campo tipo blob en el cual he logrado guardar y actualizarlo pero lo que quiero es poder recuperar esta imagen y claro poder manipularla de distintos tamaños para poderla mostrar al usuario en un jsp alguien sabe como hacer esto para ser un poco mas claro quiero hacer algo al estilo metroflog o esas páginas de redes sociales, saludos y cualquier ayudar será muy buena. Muchas gracias por adelantado

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de ezamudio

request separado

Si no tienes un framework que se encargue de manejar este tipo de cosas y lo estás haciendo con JSP pelón, necesitas crear un servlet que reciba un GET con el id (llave primaria) de la imagen a desplegar y devuelva una respuesta con el tipo MIME de la imagen y escriba la imagen (el blob) al outputstream de la respuesta.

En mi opinión no es muy recomendable almacenar imágenes en la base de datos; es mejor tenerlas en filesystem (probablemente en un directorio bajo htdocs si es que todo mundo debe poder verlas) y guardar en la base de datos la ruta hacia la imagen. De ese modo solamente generas el URL relativo a la imagen y lo pegas dentro del tag de <img/>

Re: request separado

Generalmente sugiero exactamente lo contrario: almacenar las imágenes en la base de datos. Al menos en VMS su filesystem tenía soporte *nativo* para distintos tipos de archivos (stream, indexed, sequential, relative), pero en Unix y Windows, los archivos no son más que un chorizo de bytes.

Saludos

Javier Castañón

Imagen de ezamudio

Performance

Las imágenes en filesystem las puede servir directo el web server, sin pasar por la aplicación (si es que son imágenes públicas; si es algo controlado o que requiere permisos entonces tienen que pasar a fuerzas por la aplicación).

Con las imágenes en base de datos, forzosamente tiene que sacarlas la aplicación de ahí para poder enviarlas al browser. En caso de tenerlas en base de datos, qué recomiendas Javier para no afectar el performance de todo el RDBMS? una base de datos aparte, una tabla aparte, tablespace dedicado para eso...?

Uhm...

Creo yo que hablar de soluciones "preferidas" va a ser muy dificil si entramos en todos los contextos posibles. Honestamente almacenarla en una base de datos o en sistema de archivos, por el propósito de rendimiento no es muy diferente, ambos necesitarán una posible búsqueda en la base de datos, y de E/S para poder leer ya sea la imagen del sistema de archivos o bien el archivo de datos de la tabla en cuestión, que por cierto puede también estar en un sistema de archivos similar al de la primera solución.

Por otro lado si tomamos el problema y lo asociamos al contexto de espacio a ocupar, cantidad de imagenes y tamaño de imagenes podremos observar que se tienen que tomar otro tipo de decisiones y cuestiones que incluyen aspectos del sistema de archivos anfitrión. Por ejemplo, si las imágenes son pequeñas en cuanto a tamaño (ya que tamaño de la imagen en cuanto a sus proporciones también se ve reflejado en el tamaño en bytes del chunk de bytes que ocupará), y la cantidad que se van a almacenar no son muchas, en realidad no tiene tanta diferencia en cuanto al E/S necesario como ya mencioné, sin embargo, ezamudio tocó un tema interesante... quién más y de que forma van a acceder estas imagenes? serán referenciadas por URLs o son solo para fines históricos que serán analizadas por otros medios, algún ETL, etc? Del otro modo... el sistema de archivos presenta limitantes en cuanto a la cantidad de archivos y directorios puedes tener en un árbol, desde sistemas de archivos centralizados, hasta de red (hablando un poco de NFS), hasta los distribuidos. Para ese contexto, una base de datos, tomando en cuenta que es una decente, puede no presentar tal limitante, y tienes la característica de algunas de poder particionar información. Si es un sistema de archivos distribuido, entonces la limitante de archivos generalmente es mucho mayor a la de un centralizado (pensemos en Amazon S3 por un momento).

Si te vas por el camino del sistema de archivos, y la cantidad de imagenes a almacenar es grande millones de objetos, o al menos mas de 100K, entonces allí tendrás un problema hablando de NFS. Y tendrás que pensar en algún algoritmo para dispersar los archivos en algún árbol lógico de directorios. Si te vas por el de la base de datos, si son muchos objetos, entonces deberás ir pensando en particionar información eventualmente

Así que realmente las otras partes funcionales y no funcionales de tu requerimiento son importantes, incluso la otra parte que no hemos comentado como la de almacenar réplicas de las mismas imágenes pero mas pequeñas (thumbnails?). Qué tantas serán y si las quieres almacenar en memoria ya que ocupan mucho menos espacio, al menos para aquellas que requieren ser desplegadas continuamente. Lo mejor es analizar completamente el requerimiento, ver tus posibles opciones en cuanto a la infraestructura con la que cuentas (o puedes contar) y siempre es recomendable en escribir un pequeño prototipo del problema (elige el lenguaje que quieras, incluso Java :) ), y hacer un benchmark, encontrás muchas sorpresas, muchas veces nos preocupamos por cuellos de botella imaginarios :)

Saludos,

Re: Performance

Primero que nada, es muy oportuna la distinción que hace ezamudio: imágenes que deben pasar por la aplicación y las que no. Aquí estaríamos hablando de imágenes que deben pasar por la aplicación.

También, como dice ezamudio, es una práctica más o menos común reservar un tablespace separado para las imágenes. Nada más téngase en cuenta que el esquema de respaldo incluya el tablespace dedicado a las imágenes, de lo contrario, en caso de desastre, sólo se recuperarían el resto de los datos, pero no las imágenes.

CREATE TABLE print_media_new
    ( product_id        NUMBER(6)
    , ad_id             NUMBER(6)
    , ad_composite      BLOB
    , ad_sourcetext     CLOB
    , ad_finaltext      CLOB
    , ad_fltextn        NCLOB
    , ad_textdocs_ntab  textdoc_tab
    , ad_photo          BLOB
    , ad_graphic        BFILE
    , ad_header         adheader_typ
    , press_release     LONG
    ) NESTED TABLE ad_textdocs_ntab STORE AS textdocs_nestedtab_new
    LOB (ad_sourcetext, ad_finaltext) STORE AS
      (TABLESPACE example
       STORAGE (INITIAL 6144)
       CHUNK 4000
       NOCACHE LOGGING);

Creating a Table: LOB Column Example
http://download.oracle.com/docs/cd/E11882_01/server.112/e10592/statement...

Oracle tiene un API muy rica para la manipulaciónde BLOBs, por lo que mi primera opción si la infraestructura está basada en Oracle, es utilizar el propio Oracle para almacenar las imágenes:

http://www.oracle.com/pls/db112/portal.portal_db?selected=7&frame=#oracl...

Ahora bien, manipular una imagen para obtener "thumbnails" es una muy mala idea. El proceso consume muchos recursos de CPU. Lo mejor es manipularla una sola sola vez y almacenarla en los tamaños requeridos.

Si la infraestructura está basada sobre una base de datos que no sea Oracle, entonces optaría por utilizar otro tipo de almacenamiento, (aunque no en el filesystem directamente). Actualmente existen productos como Apache Hadoop o Hypertable que proporcionan servicios de almacenamiento menos "crudos". No importaría que unos datos estuvieran en la base de datos y otros en otro medio. La información en redes sociales puede considerarse como poco relacional, o al menos, muy poco transaccional.

Saludos

Javier Castañón

Imagen de ezamudio

Hadoop?

Hadoop para almacenar? con el HDFS? pero no se supone que no es muy rápido? la ventaja es que puede estar distribuido y por lo tanto es bastante escalable, pero no sería mi primera opción para almacenar imágenes. Si es una gran cantidad de imágenes, no sería mejor tener uno de esos servidores de almacenamiento y montarlo en filesystem, echar todo ahí, y que dicho servidor tenga su propio mecanismo de respaldo?

Re: Hadoop?

Suena raro, pero sí.

Si se tiene una SAN, (la cual podría ser accedida por medio de fibra óptica o un enlace de GB Ethernet), muy probablemente se tenga un RAC de Oracle. Ambos son arquitecturas "shared everything" de escalabilidad vertical.

Para una arquitectura "shared nothing" de escalabilidad horizontal, por muy lento que sea el acceso a Hadoop su efecto no será tanto como el de la latencia en Internet.

Saludos

Javier Castañón

Imagen de Jvan

Interesante

Leyendo por ahí me encontré con esto: FileStream SQL Server es un atributo para el tipo de dato "VARBINARY" que sacó microsoft en SQLServer. La idea es almacenar las imágenes (pueden ser otros archivos) en el FileSystem, pero relacionado a la BD gracias a SQLServer, de tal manera que SQLServer administra la seguridad, por ejemplo, haciendo posible el acceso a los archivos únicamente a travez del SQLServer. Me parece una buena opción, igual me gustaría saber su opinión al respecto y si conocen algo similar para postgreSQL o para MySql.

Imagen de ezamudio

FileStream SQL Server

Interesante, aunque al final parece que tienes lo peor de dos mundos, no? El archivo se encuentra en filesystem pero ni sabes bien dónde (lo cual dificulta respaldos) y además para obtener los datos tienes que hacerlo a través de un InputStream que viene del RDBMS.

Mi propuesta inicial de tener los archivos directamente bajo el htdocs es que sean accesibles directamente al web server. Pensando en una aplicación web (porque el post original menciona jsp), esto es conveniente porque entonces la aplicación web no ocupa nada de memoria en procesar esa imagen (o PDF o lo que sea), sino que solamente toma la ruta (de la base de datos por ejemplo) y la pone dentro de la página (en un tag de IMG o de EMBED, etc); la manda al navegador y ya éste solicitará el recurso al web server, que lo puede despachar de manera directa por ser contenido estático, además que puedes ponerle cache para que en peticiones posteriores el navegador ya no tenga que bajar esa imagen del servidor (ahorrándole así trabajo y ancho de banda al web server Y a la aplicación web).

Imagen de rodrigo salado anaya

JavaFx, Java, MySQL(BLOB, IMAGENES)

Mira garoalex_85 checa estas ligas y con esto ya la hiciste para divertirte un buen rato. Te dejo un ejemplo en javafx, pero las clases java solitas te ayudan con lo de descargar la imagen.

http://dev.mysql.com/doc/refman/5.0/es/blob.html
http://mmmysql.sourceforge.net/ Lo siento no se como hacerlo con ORCLE
http://mmmysql.sourceforge.net/doc/mm.doc/book1.htm
http://java.sun.com/j2se/1.4.2/docs/api/java/io/InputStream.html
http://javafx.com/docs/tutorials/mediabrowser/module1_task2.jsp

Por otro lado lee esto:
http://java.sun.com/javafx/1/docs/api/javafx.io.http/javafx.io.http.Http...

JavaFx: Con esto puedes manipular el tamaño, y guardarlo como cache, puedes descargar la imagen en tu carpeta temporal, o ya sabras que hacer..

package imagenblob;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.image.Image;

/**
 * @author Rodrigo Salado Anaya
 */

cargaFoto { };// carga la imagen (java1.jpeg), desde etc...
descargaFoto { };// descarga la imagen(java2.jpeg), desde etc...
Stage {
    title: "JAVA FX BLOB MYSQL JAVA "
    scene: Scene {
        width: 300
        height: 100
        content: [
            ImageView {
                x: 0
                y: 0
                fitWidth: 40 //  reduce/aumenta la imagen a lo ancho
                fitHeight: 40 // reduce/aumenta la imagen a lo alto
                image: Image { url: "{__DIR__}java2.jpeg" }
                cache: true // la guarda en el cache
            }
        ]
    }
}

Java: Esta clase guarda la imagen o foto o lo que se te de la gana, en tu datacenter o máquina local:

package imagenblob;

import java.io.FileInputStream;
import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

/**
 *
 * @author Rodrigo Salado Anaya
 */

public class cargaFoto {

    cargaFoto() throws Exception {
       
        /**
         * CREATE TABLE fotos(id varchar(20), nombre varchar(20), foto BLOB);
         */

        Class.forName("org.gjt.mm.mysql.Driver");
        Connection conexion = DriverManager.getConnection("jdbc:mysql://localhost/foto", "root", "root");
        FileInputStream fis = null;
        PreparedStatement ps = null;
        conexion.setAutoCommit(false);
        File file = new File("/home/rune/NetBeansProjects/imagenBlob/java1.jpeg");
        System.out.println(file.getAbsolutePath());
        fis = new FileInputStream(file);
        ps = conexion.prepareStatement("INSERT INTO fotos(id, nombre, foto) values (?, ?, ?)");
        ps.setString(1, "foto-1");
        ps.setString(2, "java1.jpeg");
        ps.setBinaryStream(3, fis, (int) file.length());
        ps.executeUpdate();
        conexion.commit();
        ps.close();
        fis.close();
        conexion.close();
    }
}

Java: esta clase descarga el archivo.

package imagenblob;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

/**
 *
 * @author Rodrigo Salado Anaya
 */

public class descargaFoto {

    descargaFoto() throws Exception {

        /**
         * +--------+-------------+------+-----+---------+-------+
         * | Field  | Type        | Null | Key | Default | Extra |
         * +--------+-------------+------+-----+---------+-------+
         * | id     | varchar(20) | YES  |     | NULL    |       |
         * | nombre | varchar(20) | YES  |     | NULL    |       |
         * | foto   | blob        | YES  |     | NULL    |       |
         * +--------+-------------+------+-----+---------+-------+
         */

        Class.forName("org.gjt.mm.mysql.Driver");
        Connection conexion = DriverManager.getConnection("jdbc:mysql://localhost/foto", "root", "root");
        PreparedStatement stmt = conexion.prepareStatement("SELECT id, nombre, foto FROM fotos ");
        ResultSet rtst = stmt.executeQuery();
        while (rtst.next()) {
            String id = rtst.getString("id");
            String name = rtst.getString("nombre");
            File photo = new File("/home/rune/NetBeansProjects/imagenBlob/src/imagenblob/java2.jpeg");
            FileOutputStream fos = new FileOutputStream(photo);

            byte[] buffer = new byte[1];
            InputStream is = rtst.getBinaryStream("foto");
            while (is.read(buffer) > 0) {
                fos.write(buffer);
            }
            fos.close();
        }
        conexion.close();
    }
}

Me dices como te fue ok.

Solo un comentario:
Es divertido ayudar y todo eso pero checa esto va: http://www.javamexico.org/tema/lineamientos_de_publicacion
En especial la parte que empieza por: Si estás publicando una duda en los foros de discusión, te recomendamos investigar sobre el tema antes de publicar tu pregunta... etc.

Imagen de Carlos Tapia

Redimencionar

Con respecto a cambiar el tamaño, espero y este código te pueda ayudar:

private byte[] ajustaImagen(InputStream imagen,int IMG_WIDTH,int IMG_HEIGHT) throws Exception{
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                try{
                BufferedImage originalImage = ImageIO.read(imagen);
                int type = originalImage.getType() == 0? BufferedImage.TYPE_INT_ARGB : originalImage.getType();
               
                BufferedImage resizedImage = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, type);
                Graphics2D g = resizedImage.createGraphics();
                g.drawImage(originalImage, 0, 0, IMG_WIDTH, IMG_HEIGHT, null);
                g.dispose();   
                g.setComposite(AlphaComposite.Src);
         
                g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
                g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
               
                ImageIO.write(resizedImage, "Imagen", baos);
                }catch(Throwable ex){
                        throw new Exception("Error proceso Tamaño Imagen "+ ex.toString(), ex);
                }
                return baos.toByteArray();
        }

En mi caso, cambio el tamaño de la imagen antes de guardarla en la base de datos. Pero puedes hacerlo cuando quieras, en mi caso uso Spring, y por instrucción de seguridad las imágenes se guardan en la BD, no importa si es o no más eficiente, las tengo que guardar.

Mi problema es mostrarlas, en mi caso es un arreglo de imágenes, pero no se como hacerlo, ya que no puedo andar enviando petición al MVC por cada imagen, alguna idea.