Descargar un fichero de una BD con Struts2

Bueno, parece que en a descarga de ficheros tenía problemas con el IE7, así que tuvimos que reformarla, y aprovecho para contaros un poco la experiencia.

Lo primero, usar el resultado «stream» que nos proporciona struts… de manera que la configuración de la acción queda:

<action name=«download» class=«es.uva.aplicaciones.download.struts2.DownloadAdjunto»>

<result name=«success» type=«stream»>

<param name=«contentType»>application/msword</param>

<param name=«inputName»>inputStream</param>

<param name=«contentDisposition»>filename=»PLBValladolid.doc»</param>

<param name=«bufferSize»>1024</param>

</result>

<result name=«error»>/error.jsp</result>

</action>

La clase debe tener el método getInputStream o el correspondiente a la variable InputName que hemos puesto en el struts.xml. En el método execute es donde debemos cargar el fichero de la base de datos y asignarle al inputStream. En mi caso es coger un tipo blob de MySql a través de hibernate.


private InputStream inputStream=null;

public InputStream getInputStream() {
return inputStream;
}

public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}

public String execute() {
HttpServletRequest request = (HttpServletRequest) ServletActionContext
.getRequest();
HttpServletResponse response = (HttpServletResponse) ServletActionContext
.getResponse();

if (request.getParameter(«id») != null) {
String id = request.getParameter(«id»);
file = dao.findAdjunto(id);
} else {
// ERROR: id no encontrado
addActionError(rb.getString(«errors.sinparametro»));
return ERROR;
}
if (file != null) {
response.setContentType(file.getTipo());
response.setContentLength(file.getSize().intValue());
response.setHeader(«Content-Disposition»,»inline; document.fileName=» + file.getNombre() + «»);
response.setHeader(«Cache-Control»,»must-revalidate, post-check=0, pre-check=0″);
response.setHeader(«Pragma», «public»);
response.setDateHeader(«Expires», 0);

Blob blob = file.getDatos();
try {
this.setInputStream(blob.getBinaryStream());
} catch (Exception e) {
System.err.println(e);
addActionError(rb.getString(«errors.escrituraencliente»));
return ERROR;
}
return SUCCESS;
} else {
//Fichero no encontrado
addActionError(rb.getString(«errors.ficheronoencontrado»));
return ERROR;
}
}

Java FDF: Rellenar y procesar formularios pdf

Para mí ha sido todo un descubrimiento. Con FDF puedes rellenar automáticamente los formularios PDF sin ningún problema, y lo más interesante, procesarles. En nuestro caso lo hemos hecho todo a través de J2EE, con Servlets y lo más divertido a sido el tema de los flags de los campos para decir si eran obligatorios, ocultos, sólo lectura, etc.

Los flags F son de anotación, y la posición de los bits son:

  1. invisible
  2. Oculto
  3. Imprimir el campo.
  4. No Zoom
  5. No rotar
  6. No ver
  7. Solo lectura.

Las combinaciones de estos bits nos permiten activando el oculto por ejemplo que nadie vea un campo de un formulario que usamos para almacenar el identificador del formulario, etc.

Los otros flags interesantes son los Ff, de campo:

  1. Sólo lectura, para que el usuario no pueda modificarlo.
  2. Obligatorio, para que el usuario tenga que rellenarlo antes de su envío.
  3. No exportar, es decir, que no se va a enviar al realizar el envío del formulario

Al final, en nuestro servlet tenemos en el doGet el autorellenado de formulario, ya que el usuario se a autenticado previamente, y le rellenamos los datos de nombre, apellido, etc para facilitarle la tarea. El código sería:

public void doGet(HttpServletRequest req, HttpServletResponse res)
throws IOException, ServletException {
FDFDoc outputFDF = null;
/* Create a new FDF. */
outputFDF = new FDFDoc();
outputFDF.SetFlags("AutorizadorUID", FDFItem.FDFSetF, 2); //Invisible
outputFDF.SetFlags("SolicitanteNombre", FDFItem.FDFSetFf, 1); //Solo lectura
outputFDF.SetFlags("SolicitudDireccionesIP", FDFItem.FDFSetFf, 2); //Obligatorio
outputFDF.SetFlags("SolicitudDireccionesIP", FDFItem.FDFClearFf, 1); //Escribible
try {
outputFDF.SetFile(fichPdf);
fillPDF(outputFDF, formulario);
res.setContentType("application/vnd.fdf");
OutputStream out = res.getOutputStream();
outputFDF.Save(out);
out.close();
} catch (FDFException e) {
/* We handle an error by emitting an html header */
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("Caught FDF exception");
out.println(e.toString());
System.err.println("Error "+e);
e.printStackTrace(out);
}
}

En cuanto al procesado del formulario tenemos algo parecido:

try {
FdfInput = null;
// get the length of incoming data
int howMany = req.getContentLength();
byte data[] = new byte[howMany];
// read data into byte array
req.getInputStream().read(data);
// create FDFDoc from data
FdfInput = new FDFDoc(data);
//Get ComunicadorUID value
String comunicadorUID=FdfInput.GetValue("ComunicadorUID");
} catch (FDFException e) {
reportError(req, res, "Error al generar el pdf. " + e.toString());
// Pte: DEjar un log del error
}

y luego obtenidas las variables del formulario, podemos guardarlo en una base de datos, mandar un correo electrónico, generar otro formulario, etc, etc.

Sinceramente, no me esperaba un tratamiento tan sencillo de los pdf’s. Es una muy buena idea, pero con una limitación, necesitamos acrobat reader 7 o superior, sino este invento no funciona.