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


5 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.

danguer 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

Luis M. dijo...
El autor ha eliminado esta entrada.
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