jueves, 1 de octubre de 2009

Imprimir reporte de Jasperreports en el Cliente

Les traigo un ejemplo de como imprimir directamente a la impresora del cliente un reporte generado con jasperreport en el servidor.

Para poder invocar a la impresora del cliente tenemos que hacer un applet y firmarlo ya que esta restringido y no puede invocar recursos del cliente, por eso debemos firmarlo, este applet contiene una clase que va a recibir los datos que se generan en el servidor.

Este codigo que les proporciono ha sido probado con la version 3.0 de jasperreports.

Primero tenemos el codigo del Servlet que nos genera los reportes , bueno en este caso como estaba haciendo pruebas genere unos archivos.xml con ireport que contienen datos de un reporte, lo que tiene que hacer es cambiar esa parte por la generacion de sus reportes, este metodo lo invoco en doPost y doGet

protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

int in;

try {
/*Esta parte la deben de cambiar de acuerdo al numero de reportes que tiene que

imprimir, deberia de funcionar con uno o mas reportes*/
int numFiles = 3;
OutputStream outStream = response.getOutputStream();
DataOutputStream dataOut = new DataOutputStream(outStream);
//envio el numero de archivos a enviar
dataOut.writeInt(numFiles);
/* Temporal */
File file = new File("C:\\prueba1.xml");
File files [] = new File [numFiles];
files[0] = file;
files[1] = new File("C:\\prueba2.xml");
files[2] = new File("C:\\prueba3.xml");

/*Lo de arriba son mis archivos temporales, esto lo pueden cambiar generando archivos temporales en el servidor con jasperreport y luego los envian al cliente otra opcion es llenar su reporte y obtener los bytes para mandarlos al cliente, tiene que llenar sus repores con JRPrintXmlLoader, ejemplo

JasperReport reporte = (JasperReport) JRLoader.loadObject("reporte1.jasper");
JasperPrint jasperPrint = JasperFillManager.fillReport(reporte, null, conection);
JRExporter exporter = new JRXmlExporter();
exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);

y en el applet vamos a recuperar el (los) objeto jasperPrint para mandarlos a la impresora

*/
FileInputStream fileStrm = null;
BufferedInputStream bufferStrm = null;
byte fileBytes[] = null;
for (int k = 0; k < numFiles; k++ ){
/*logger.info(" enviando el archivo no. " + k + " nombre Archivo " + files[k].getName()
+ " tamaño" + files[k].length());
*/
//envio nombre del archivo
dataOut.writeUTF(files[k].getName());
//envio tamño del archivo
dataOut.writeLong(files[k].length());
//abrimos el archivo a enviar
fileStrm = new FileInputStream(files[k]);
bufferStrm = new BufferedInputStream(fileStrm);
//Envia el archivo
while ((in = bufferStrm.read()) != -1){
dataOut.write(in);
}

// if (logger.getLevel().INFO )
// logger.info("Archivo enviado :: " + files[k].getName() );

outStream.flush();
fileStrm.close();
bufferStrm.close();
}


outStream.close();

dataOut.close();
outStream = null;
bufferStrm = null;
fileStrm = null;
dataOut = null;

Ahora el codigo de el applet, como dije antes en este applet vamos a recibir los datos de los objetos jasperPrint y despues los mandamos a imprimir

/*
* AppletPrint.java
*
* Created on 1 de octubre de 2009, 12:40 PM
*/

package mx.com.lmp.erp.print;

import java.awt.BorderLayout;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExporterParameter;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperPrintManager;
import net.sf.jasperreports.engine.export.JRPrintServiceExporter;
import net.sf.jasperreports.engine.export.JRPrintServiceExporterParameter;
import net.sf.jasperreports.engine.print.JRPrinterAWT;
import net.sf.jasperreports.engine.xml.JRPrintXmlLoader;
import net.sf.jasperreports.view.JRViewer;

public class AppletPrint extends javax.swing.JApplet {

/** Initializes the applet AppletPrint */
public void init() {
try {
this.urlService = getParameter("urlService");
java.awt.EventQueue.invokeAndWait(new Runnable() {
public void run() {
initComponents();
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
}


private void initComponents() {
jPanel1 = new javax.swing.JPanel();
jBtnImprimir = new javax.swing.JButton();

setLayout(new java.awt.BorderLayout());

jBtnImprimir.setText("Imprimir");
jBtnImprimir.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jBtnImprimirActionPerformed(evt);
}
});

jPanel1.add(jBtnImprimir);

add(jPanel1, java.awt.BorderLayout.CENTER);

}

private void jBtnImprimirActionPerformed(java.awt.event.ActionEvent evt) {
comunicacionServer();//para recibir los datos del server
}


// Variables declaration - do not modify
private javax.swing.JButton jBtnImprimir;
private javax.swing.JPanel jPanel1;
// End of variables declaration
//Guardara la url del servlet al que se comunicara para imprimir
private String urlService;

private void comunicacionServer(){

int in=0;
long tamFile=0;

/* Primero creamos la URL para la conexión. Tiene sentido construir la
dirección de esta forma tan "complicada" puesto que el applet solo puede
establecer conexiones con su servidor, y así, al construir la dirección
dinámicamente, no tenemos que retocar el código al irnos a otro servidor.
En todo caso, lo siguiente sería válido:
URL direccion = new URL ("http://www.javahispano.com/servlet/MiServlet");
*/
URL pagina = this.getCodeBase();
String protocolo = pagina.getProtocol();
String servidor = pagina.getHost();
int puerto = pagina.getPort();
//String servlet = "/servlet/MiServlet";/ServletEnviaReport
String servlet = "";
if ( this.urlService != null && !this.urlService.equals(""))
servlet = this.urlService;
else
servlet = "/PruebaApplet/ServletEnviaReportes";

URL direccion = null;
URLConnection conexion = null;
try {
direccion = new URL(protocolo, servidor, puerto, servlet);
conexion = direccion.openConnection();
System.out.println(" Url del urlService " + direccion.toString());
} catch (IOException ex) {
ex.printStackTrace();
}

/*Lo siguiente es decirle al navegador que no use su
cache para esta conexión, porque si lo hace vamos a
tener un página estatica, y para eso no nos metemos
en estos líos ;-). */
conexion.setUseCaches(false);

/* Ahora añadimos todas las cabeceras de HTTP que necesitemos, Cookies, contenido,
autorizacion, etc. con el método:
conexion.setRequestProperty ("cabecera", "valor");
Consultar la especificación de HTTP para más detalles. Por ejemplo, para decir
que preferentemente hablamos español: */

conexion.setRequestProperty("Accept-Language", "es");


BufferedInputStream buffer = null ;
try {


/* Procesamos la información de la forma adecuada, según se
trate de datos ASCII o binarios. */
//Obtenemos el stream de entrada para leer la informacion que nos envie el
//server
DataInputStream dataIn = new DataInputStream(conexion.getInputStream());
buffer = new BufferedInputStream(conexion.getInputStream());
// Recibo el numero de archivos que enviara el servidor
int numArchivosRecibir = dataIn.readInt();
System.out.println(" Numero de archivos para imprimir " + numArchivosRecibir);

// Recibe los archivos
String nameFiles[] = new String[numArchivosRecibir];
File f = null;
File files[] = new File[numArchivosRecibir];
FileOutputStream ouputStream = null;

for(int k = 0; k < f =" this.fileTemp(dataIn.readUTF(),null);" tamfile =" dataIn.readLong();" ouputstream =" new" j =" 0;" datain =" null;" buffer =" null;" ouputstream =" null;" inputstream =" null;" jasperprintlist =" new" j =" 0;" inputstream =" new">

/*Aqui es donde se cargan los datos recibidos del server y se lanza a la impresion*/
JasperPrint jsprint = JRPrintXmlLoader.load(inputStream);
JasperPrintManager.printReport(jsprint,true);
}

} catch (JRException e1) {
e1.printStackTrace();
}

//borramos los archivos temporales
for(File fi : files ){
fi.delete();
}

} catch (IOException ex) {
ex.printStackTrace();
}
}

public void ejecutaConJavascript (final String urlConParametros){

AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
try {
comunicacionServer(urlConParametros);
} catch(Exception e) {
e.printStackTrace();
return false;
}
return null;
}
});
}

public void comunicacionServer(String urlConParametros) {
this.urlService = urlConParametros;
this.comunicacionServer();
}

private File fileTemp (String name, String suffix) {
try {
return File.createTempFile(name,suffix);
} catch (IOException ex) {
ex.printStackTrace();
return null;
}
}


}

Bueno el codigo me base en un applet que encontre en javahispano, la parte importante es el metodo comunicacionServer() en este metodo se reciben los arhivos o datos que nos envia el server y despues se lanzan a la impresora, tambien tiene un metodo ejecuta con javascript este metodo es util para poder armar nuestra url con los parametros que necesitemos para generar nuestros reportes, ahora un codigo de ejemplo que iria en el jsp :


<applet name="nomApplet"
code="mx.com.lmp.erp.print.AppletPrint"
archive="AppletPrintJRV.jar,jasperreports-3.0.0.jar,commons-digester-1.7.jar,
commons-javaflow-20060411.jar,commons-logging-1.0.2.jar,commons-logging-api-1.0.2.jar,
commons-beanutils-1.7.jar,commons-collections-2.1.jar"
width="100" height="100">
<param value="/WebApplication2/ServletReport" name="urlService">
</applet>

como pueden ver le estoy poniendo algunas libs y la de jassperreport bueno esto lo hice para que el applet pueda encontrar las libs necesarias tambien firme el jar de jasperreports ya que como observan en el codigo genero archivos temporales, si no lo hago asi pues no podria leer esos archivos temporales y pues otra opcion seria que en lugar de guardar los datos que recibo del server en temporales pues tomarlos de memoria y cargalos con JRPrintXmlLoader para generar el jasperPrint y enviarlo a la impresora (creo que lo intentare despues).

Con javascript seria algo asi :


<script type="text/javascript" language="JavaScript">
function ejecutoApplet() {
//aqui hay que mandarle el nombre que le dimos al applet
var Myapplet = document.applets['nomApplet'];
//alert(Myapplet);
var p = Myapplet.ejecutaConJavascript('/WebApplication2/ServletEnviaReport');
//alert(p) //si el metodo del applet retorna algo con esto podemos ver el resultado
}
</script>

Bueno pues eso es todo por el momento en cuanto mejore el codigo actualizo este post o hago uno nuevo, les dejo mi codigo de ejemplo en la siguientes ligas applet, aplicacion web de ejemplo, firmas del applet

No olviden firmar el applet y colocarlo en su aplicacion si le hacen algun cambio pues de lo contrario les pueden salir problemas de permisos, espero les sirva

4 comentarios:

Anónimo dijo...

hola neo_lestad tengo un problema la verdad soy nuevo en esto de java y reports me gusta pero esto si me esta costando el problema es el siguiente yo tengo un jasper el cual lo lleno de la sig forma: JasperPrint print =JasperFillManager.fillReport(iCompDot,tImprimirComp.getParams(),tImprimirComp.getJRDataSource());JasperPrintManager.printReport(print, false); el problema es que este imprime sobre el server apreciaria mucho tu ayuda es que no entiendo muy bien tu codigo esto lo necesito imprimir desde el cliente y tambien tengo un jsp pero no me quedo claro lo del applet porfas mi correo es jonybravo117@gmail.com esto lo estoy realizando en eclipse gracias

ruandischer tee dijo...

Para los usuarios empresariales, la piedra angular de la inteligencia de negocios es la habilidad de ver la información que necesitan lo más rápido y tan claramente como sea posible. Los informes de que son demasiado complejos para crear o utilizar pronto pierden valor. JasperReports, usted puede diseñar, implementar o integrar la potencia de los informes de Jaspersoft en sus aplicaciones o independiente de información medio ambiente.

Unknown dijo...

estas lineas de codigo estan mal

for(int k = 0; k < f =" this.fileTemp(dataIn.readUTF(),null);" tamfile =" dataIn.readLong();" ouputstream =" new" j =" 0;" datain =" null;" buffer =" null;" ouputstream =" null;" inputstream =" null;" jasperprintlist =" new" j =" 0;" inputstream =" new">


podes pasar el archivo completo

Adolfo Villanueva dijo...

El link del aplictivo web no funciona o esta mal, te agradezco lo publiques nuevamente.