Guardar imágenes en Base de Datos MySQL

De ChuWiki

Guardar y leer imágenes en Base de Datos[editar]

He metido todos los artículos de esta wiki y de http://chuidiang.org relativos a Java y JDBC en un pdf Libro JDBC Java. A través del enlace puedes comprarlo si tienes interés.

Veamos aquí un ejemplo de cómo guardar y leer una imagen de una base de datos MySQL. Para ello, usaremos un campo blob en la base de datos. Puedes ver el ejemplo completo en ImagenesEnBaseDatos.java


Creación de la tabla[editar]

Primero crearemos una tabla en la base de datos, en la que pondremos una clave primaria, un campo de texto tinytext(256 caracteres) para el nombre de la imagen y un campo imagen blob (65535 bytes) para los bytes de la imagen.

mysql> create table fotos (id int auto_increment,
    -> primary key (id),
    -> nombre tinytext,
    -> imagen blob);


Conexión con la base de datos[editar]

La conexión de la base de datos se hace de la forma normal en que se hace la conexión de java con MySQL. El siguiente trozo de código muestra cómo, suponiendo que hay el servidor de MySQL se encuentra en el mismo ordenador en el que vamos a correr el ejemplo (localhost), que tiene una base de datos "hibernate", con usuario "hibernate" y password "hibernate".

    private Connection conexion = null;
    ...
    private void estableceConexion() {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conexion = DriverManager.getConnection(
                    "jdbc:mysql://localhost/hibernate", "hibernate",
                    "hibernate");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


Escribir la imagen en la base de datos[editar]

Supongamos que tenemos un fichero de imagen paisaje.jpg en el directorio de ejecución. Lo primero que haremos será abrir el fichero y obtener algún tipo de InputStream (en el ejemplo un FileInputStream) que nos permita ir leyendo bytes del fichero.

FileInputStream is = new FileInputStream("./paisaje.jpg");

Luego creamos un PreparedStatement, en el que tengamos dos parámetros para las dos columnas de la tabla que tenemos que rellenar: el nombre del fichero y el campo blob con los bytes de la imagen.

PreparedStatement st = conexion.prepareStatement("insert into fotos values(null,?,?)");

Tenemos ahora que rellenar los dos interrogantes de la PreparedStatement con los datos concretos que queremos insertar en base de datos. El primero será el nombre del fichero imagen y lo rellenaremos con setString()

st.setString(1, "paisaje.jpg");

El segundo debería ser el array de bytes de la imagen. Afortunadamente y por necesidad si es un fichero muy grande, no tenemos que cargarlo todo en memoria. Basta llamar al método setBlob() pasándole el InputStream del fichero. De esta forma, cuando hagamos la inserción, el PreparedStatement se encargará de ir leyendo bytes e insertándolos en base de datos.

st.setBlob(2, is);

Finalmente, hacemos efectiva la inserción y cerramos todo lo que tenemos abierto

st.execute();
is.close();
st.close();


Lectura de la imagen de la base de datos[editar]

Ahora vamos al proceso contrario. Vamos a leer la imagen de la base de datos y guardarla en un fichero. Una vez establecida la conexión con la base de datos, preparamos el select de consulta y lo ejecutamos

PreparedStatement st = conexion.prepareStatement("select * from fotos");
ResultSet rs = st.executeQuery();

En la lectura, tampoco queremos cargar todos los bytes de la imagen de golpe en memoria, así que en vez de usar el método getBytes() del ResultSet, que nos daría todos los bytes de la imagen de golpe, usaremos el método getBlob(), que nos devuelve una clase Blob de la que podremos ir leyendo bytes poco a poco.

while (rs.next()) {
   String nombre = rs.getString(2);

   Blob bytesImagen = rs.getBlob(3);
   InputStream is = bytesImagen.getBinaryStream();

Ya tenemos un InputStream del que ir leyendo bytes. Ahora sólo nos queda abrir un FileOutputStream e ir copiando de uno en otro por medio de un bucle. Para el nombre del fichero, usaremos el que leemos de base de datos, pero precedido por "copia_", para no machacar el original, al que suponemos todavía en el disco en este ejemplo. Al terminar la copia, cerramos todo, tanto el InputStream del Blob, como el FileOutputStream

FileOutputStream fw = new FileOutputStream("copia_" + nombre);

// Bucle de lectura del blob y escritura en el fichero, de 1024
// en 1024 bytes.
byte bytes[] = new byte[1024];
int leidos = is.read(bytes);
while (leidos > 0) {
   fw.write(bytes);
   leidos = is.read(bytes);
}
fw.close();
is.close();

Y ya tenemos en copia_paisaje.jpg nuestra copia de la imagen, extraída de la base de datos.