lunes, 17 de noviembre de 2008

Insertar imagenes en una base de datos en una aplicacion web

Que tal, en esta primer entrada veremos como insertar una imagen en la base de datos y también como recuperarla y mostrarla dentro de nuestra aplicación web. Para hacer esto, haremos uso de un par de jar's de jakarta-commons, no hay que re-inventar la rueda.

Lo que necesitamos:

- eclipse (con wtp para mayor comodidad)
- apache tomcat 6 (o algun otro contenedor de servlets)
- commons-fileupload-1.2.jar
- commons-io-1.3.1.jar
- mysql-connector-java-5.1.5-bin.jar (Este driver depende de su motor de base de datos)
- MySql 5.0.67 (Esta es la bd que uso, podrían ocupar cualquier otra que soporte datos BLOB)
- Opcionalmente: log4-1.2.11.jar, jstl.jar, standar.jar (las ultimas dos vienen en los ejemplos de apache-tomcat 6, en versiones anteriores se encuentran en el directorio lib de tomcat)

No es absolutamente necesario usar eclipse, podrían realizar el proyecto con algún otro ide o sin usar algún ide.

Para insertar la imagen realice el siguiente método que pertenece a mi DAO:

public void insertaImagenProducto(int idProducto,
InputStream inps, long size) {

PreparedStatement ps=null;
try {

ps = con.prepareStatement(
"UPDATE PRODUCTOS SET IMAGEN = ? WHERE" +
" PRODUCTO_ID = ?");

ps.setBinaryStream(1, inps, (int)size);
ps.setInt(2, idProducto);
ps.executeUpdate();
ps.close();

} catch (SQLException e) {

e.printStackTrace();
}
}


En el método de insertaImagen, únicamente recibo un InputStream del cual se leerán los bytes que se insertaran en el campo BLOB, tambien se recibe el tamaño del InputStream o el numero de bytes que contiene y finalmente se llama al método setBinaryStream del objeto preparedStatment con el cual se inserta la imagen en la bd

Para recuperar un dato BLOB de la bd se realiza lo siguiente:
public byte[] obtenImagenProducto(int idProducto)  {

ResultSet rst = null;
PreparedStatement pstm = null;
byte[] buffer = null;

try {
logger.info("Datos producto id " +idProducto);
String sql = "select imagen from
productos where producto_id = ?";
pstm = con.prepareStatement(sql);
pstm.setInt(1, idProducto);
rst = pstm.executeQuery();
while (rst.next()){
Blob bin = rst.getBlob("imagen");
if (bin != null) {
InputStream inStream = bin.getBinaryStream();
int size = (int) bin.length();
System.out.println(" El tamaño en bytes " + size);
buffer = new byte[size];
int length = -1;
int k = 0;
try {
inStream.read(buffer, 0, size);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}

} catch (SQLException ex) {
logger.error("ERROR " + ex);
return null;
} finally {
/*
rst.close();
stm.close();
*/
rst = null;
pstm = null;
}
return buffer;
}
Este método obtiene un dato de tipo BLOB, si el dato es diferente de nulo entonces se crea un objeto de tipo InputStream con el cual se leerán los bytes del dato blob, se crea un arreglo de bytes de acuerdo al tamaño del blob y posteriormente se leen, una vez leídos los bytes estos simplemente se retornan en el arreglo creado, en la siguiente capa se reciben estos bytes y se envian al navegador.

Lo anterior es la parte que pertenece al modelo, ahora vamos con la parte correspondiente a la vista, en esta parte tenemos un jsp que contiene el siguiente formulario :

<form name="myform" action="./ServletProductoImagen"
method="post" enctype="multipart/form-data">

<input type="hidden" value="${param.idProducto}" name="idProducto">
Selecciona la Image:

<input type="file" name="myimage">

<input type="submit" name="Aceptar" value="Submit your files">
<form>
Bien con este formulario escogeremos el archivo que se agregara a la base de datos,
yo le paso un parámetro, el idProducto con este sabre en que registro insertar la imagen,

Ahora veamos el servlet que inserta la imagen, en el método post :
// first check if the upload request
//coming in is a multipart request
//Revisa si la peticion es multipart
boolean isMultipart = ServletFileUpload.isMultipartContent(request);

// Create a factory for disk-based file items
//Esto es parte de la lib de commons-fileupload
//y es un objeto para generar archivos temporales
FileItemFactory factory = new DiskFileItemFactory();

// Create a new file upload handler
//Aqui crea un objeto para manejar la peticion
//que llega con un archivo
ServletFileUpload upload = new ServletFileUpload(factory);


// parse this request by the handler
// this gives us a list of items from the request
List items = null;
try {
//Se obtienen los items de la forma
//es decir obtenemos los parametros de la forma
items = upload.parseRequest(request);
} catch (FileUploadException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String idProducto = "";
Iterator itr = items.iterator();
while(itr.hasNext()) {

//recorremos los items en busca del id
//para realizar el insert
FileItem item = (FileItem) itr.next();

if(item.isFormField()) {
// get the name of the field
String fieldName = item.getFieldName();

if(fieldName.equals("idProducto"))
idProducto = item.getString();
}
logger.info("ID Producto" + idProducto);
}
itr = items.iterator();
//ahora lo recorremos para obtener el archivo que se
//envio en la forma
while(itr.hasNext()) {
FileItem item = (FileItem) itr.next();

// check if the current item is a form field or
//an uploaded file
if(! item.isFormField() ){

// the item must be an uploaded file save it to disk.
// Note that there
// seems to be a bug in item.getName() as it
//returns the full path on
// the client's machine for the uploaded file name,
// instead of the file
// name only. To overcome that, I have used a
// workaround using
// fullFile.getName().
//Aqui es donde se obtiene el nombre del archivo, esto podriamos
//usarlo si queremos gurdar el nombre o guardar el archivo en
//alguno de nuestros directorios.
//File fullFile = new File(item.getName());
//File savedFile = new File(fullFile.getName());
try {
//Este metodo comentado escribe el archivo en nuestro
//sistema de archivos con una ruta adecuada
//item.write(savedFile);

//pero lo que nos interesa es gurdar la imagen en la bd, asi
//que invocamos a nuestro dao y le pasmos los valores
//necesarios y es todo
daoProductos.insertaImagenProducto(
Integer.parseInt(idProducto),
item.getInputStream(),
item.getSize());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

System.out.println("Archivo guardado");
}
}

RequestDispatcher r =
request.getRequestDispatcher("./index.jsp");

r.forward(request, response);
}
Aqui es donde hacemos uso de el jar commons-fileupload, con este obtenemos
el archivo que se envio al servlet a travez de la forma, revisamos por los
parametros que se encuentran en la forma, el id para saber el registro en
donde se insertara la imagen, posteriormente obtenemos un inputStream con los
bytes de la imagen y el numero de bytes. Y lo enviamos a nuestro dao para que
realize el insert en la bd.

Para terminar, para mostrar la imagen en nuestro jsp entonces invocamos al metodo
doGet de el servlet en el cual tenemos lo siguiente:
String idProducto = request.getParameter("idProducto");
System.out.println(" El id del idProducto " + idProducto);
if(idProducto!=null && !idProducto.equals("")){

int idProd = Integer.parseInt(idProducto);
response.setContentType("image/gif");
byte [] imag = this.obtieneProducto(idProd);
if(imag != null) {
ServletOutputStream out = response.getOutputStream();
out.write(imag);
}
}
Aqui le enviamos el id del registro del cual se obtendra la imagen despues colocamos el contentype del response como image, invocamos anuestro dao que nos devolvera los bytes, obtenemos el ServletOutputStream para enviar los bytes de la imagen, en nuestro jsp simplemente hariamos:
<img src="./ServletProductoImagen?idProducto=${producto.idProducto}"
alt="${producto.nombre}" height="150" width="150" >
Espero colocar el proyecto de eclipse para que puedan descargarlo solo debo ver como o donde


18 comentarios:

Anónimo dijo...

Jovenes javeros, muy bien su post, en base a lo que uno aprende pues esta es una buena solucion al problema de subir imágenes a una BD(mysql,sql,oracle), claro con su respectivo driver, hay algunos que tienen problemas con los drivers, o con los queries o hasta con el método UpLoad para obtener el archivo jeje :$ es bueno explicar las distintas formas en una aplicación web(Struts, servlets, etc), pero también es bueno que expliquemos el COMO leer el archivo y mandarlo a la BD, muy buen post, deberían hacer unos dando una explicación o ejemplo en struts y en una aplicación de escritorio.

Unknown dijo...

Como nota, hay que tener cuidado porque muchas veces los campos BLOB están limitados en la implementación, en una aplicación tuve un error de que el campo BLOB estaba limitado a 4KB por lo que con archivos pequeños funcionaba bien, pero en otros los cortaba, en ese caso usé LONGBLOB.

Anónimo dijo...

amigo me parece muy buena tu contribucion pero me pareceria aun mejor si aunque sea dieran los jar para poder descaragarlos

Nemesis Bow dijo...
Este comentario ha sido eliminado por el autor.
Anónimo dijo...

Para los que quieren descargar los archivos .jar necesarios para realizar esta operacion les recomiendo esta pagina :

http://www.jarfinder.com/index.php/

Aki al hacer click en la pestaña jar podran buscar los jares necesarios para este proyecto.Salu2

Y por ultimo, te agradeceria mucho si me dieras el proyecto a mi correo : luismiguel12311@hotmail.com

Anónimo dijo...

uaooooo me sirvio de mucho su idea me salio todo perfecto, saludos

Anónimo dijo...

mi correo es garoalex_85@live.com.mx


para lo que les pueda ayudar...

Anónimo dijo...

hola muy buen aporte yo estaba buscando hace tiempo esto, agredeceria si me envias el proyecto a mi correo porfa luis55_2@hotmail.com, gracias de antemano

Miguel Angel Cifredo dijo...

Genial. Me ha servido 100%.
Llevaba mucho tiempo buscando algo así de simple y de efectivo. GRACIAS desde Sevilla (España).

Anónimo dijo...

Que tal gente? soy nuevo en el campo de la programación en java y respecto a este post quisiera saber si puede funcionar también para subir a la BD archivos que no sean necesariamente imágenes, sino archivos de texto o pdf. Espero me respondan. Gracias

neo_lestat dijo...

Que tal, si puedes usarlo, los datos se guardan en binario, solo que para mostrarlos harias algunas cosas diferentes

José Luis Toro Alcarraz dijo...
Este comentario ha sido eliminado por el autor.
Anónimo dijo...

Hola amigo me puedes enviar el proyecto te agradeceria mucho.

mcrewjeiga@hotmail.com

Anónimo dijo...

Hola hermano excelente, me parece totalmente estupendo justo lo que necesitaba para mejorar mi proyecto. Ahora por favor serias tan amable de enviarme el proyecto completo para analizarlo. a hack10.5@hotmail.com

Unknown dijo...

hola que tal muy buen aporte , disucpa me preguntaba si me podias enviar el proyecto a mi correo es chivasjm_val@hotmail.com te lo agradeceria mucho

Anónimo dijo...

hola que tal? muy buen aporte , disculpa me podrias enviar el proyecto a mi correo es jjadb@hotmail.com
Gracias por adelantado ;)

Anónimo dijo...

Amigo me parece muy interesante tu ejemplo, pero tengo un problema al momento de emplearlo en mi proyecto me genera varios errores, me preguntaba si serias tan gentil de compartirme el código fuente muchas gracias de antemano.

mi correo es jj.garciatc@gmail.com

Jaime Alberto Arbizo dijo...

Excelente explicación.
¿Por que piden que les envien el codigo fuente al correo si ya esta detallado en el post?.