lunes, 18 de julio de 2016

Enviar documentos desde una aplicación web hacia la impresora sin pre visualización en el navegador

En este post explicare como imprimir un documento PDF directamente desde una aplicación web evitando la pre visualización por defecto que realizan los navegadores web o evitando también la descarga por defecto del documento en la maquina cliente.

Para llevar a cabo la solución, vamos a utilizar una implementación open source (Project Tyrus) de la especificación JSR 356 - Java API for WebSocket para establecer la comunicación entre la aplicación web y el servidor que deberá estar ejecutándose en la maquina cliente y que recibirá mediante WebSocket el documento a imprimir y enviar este a la impresora. A continuación los pasos a realizar utilizando el IDE IntelliJ IDEA:

PASO 1: Creación del proyecto Tyrus donde se definirá el WebSocket endpoint y el servidor




PASO 2: Agregar las dependencias maven necesarias para la creación de nuestro servidor Tyrus



    <dependencies>
        <dependency>
            <groupId>org.glassfish.tyrus</groupId>
            <artifactId>tyrus-server</artifactId>
            <version>1.13</version>
        </dependency>

        <dependency>
            <groupId>org.glassfish.tyrus</groupId>
            <artifactId>tyrus-container-grizzly-server</artifactId>
            <version>1.13</version>
        </dependency>
    </dependencies>



PASO 3: Implementación del endpoint server FileServerEndpoint

package ws.serverprinter.server;

import java.io.*;
import java.nio.ByteBuffer;

import javax.websocket.CloseReason;
import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/receive/fileserver")
public class FileServerEndpoint {

    static ByteArrayOutputStream baos = null;

    @OnOpen
    public void open(Session session, EndpointConfig conf) {
        System.out.println("FileServerEndpoint is open");
    }

    @OnMessage
    public void processUpload(ByteBuffer msg, boolean last, Session session) {
        System.out.println("ByteBuffer message");
        while(msg.hasRemaining()) {
            baos.write(msg.get());
        }
    }

    @OnMessage
    public void message(Session session, String msg) {
        System.out.println("Message: " + msg);
        if(!msg.equals("end")) {
            baos = new ByteArrayOutputStream();
        }else {
            try {
                baos.flush();
                baos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @OnClose
    public void close(Session session, CloseReason reason) {
        System.out.println("WebSocket closed: "+ reason.getReasonPhrase());
    }

    @OnError
    public void error(Session session, Throwable t) {
        t.printStackTrace();

    }
}

PASO 4: Implementación de la aplicación principal y el server Tyrus

package ws.serverprinter.server;

import org.glassfish.tyrus.server.Server;

import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
 * Created by williams on 19-07-16.
 */
public class MainApp {

    public static void main(String[] args) {
        runServer();
    }

    private static void runServer() {
        Server server = new Server("localhost", 8025, "/websockets", null, FileServerEndpoint.class);
        try {
            server.start();
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            System.out.print("Press a key to stop the server.");
            reader.readLine();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            server.stop();
        }
    }
}


PASO 5: Desarrollo de la aplicación cliente


<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Chat</title>
</head>
<body>
<h2>File Upload</h2>
Select file
<input type="file" id="filename" />
<br>
<input type="button" value="Connect" onclick="connect()" />
<br>
<input type="button" value="Upload" onclick="sendFile()" />
<script>
    var ws;

    function connect() {
        ws = new WebSocket("ws://localhost:8025/websockets/receive/fileserver");
        ws.binaryType = "arraybuffer";
        ws.onopen = function() {
            alert("Connected.")
        };

        ws.onmessage = function(evt) {
            alert(evt.msg);
        };

        ws.onclose = function() {
            alert("Connection closed...");
        };
        ws.onerror = function(e) {
            alert(e.msg);
        }

    }

    function sendFile() {
        var file = document.getElementById('filename').files[0];
        ws.send('filename:'+file.name);
        var reader = new FileReader();
        var rawData = new ArrayBuffer();
        reader.loadend = function() {}
        reader.onload = function(e) {
            rawData = e.target.result;
            ws.send(rawData);
            alert("file transferred.")
            ws.send('end');
        }
        reader.readAsArrayBuffer(file);
    }


</script>
</body>
</html>

PASO 6: Implementación del servicio de impresión


    @OnMessage
    public void message(Session session, String msg) {
        System.out.println("Message: " + msg);
        if(!msg.equals("end")) {
            baos = new ByteArrayOutputStream();
        }else {
            try {
                baos.flush();
                baos.close();
                print(baos);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (PrintException e) {
                e.printStackTrace();
            }
        }
    }

    private void print(ByteArrayOutputStream baos) throws PrintException {
        PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
        if (services.length > 0) {
            DocFlavor psInFormat = DocFlavor.INPUT_STREAM.AUTOSENSE;
            Doc myDoc = new SimpleDoc(new ByteArrayInputStream(baos.toByteArray()), psInFormat, null);
            DocPrintJob job = services[0].createPrintJob();
            job.print(myDoc, null);
        }
    }

PASO 7: Iniciamos el server Tyrus


PASO 8: Adjuntamos algún archivo PDF desde nuestra aplicación cliente y lo enviamos al server


Se puede observar como el archivo se envió directamente a la impresora.

Fuentes:
https://tyrus.java.net/documentation/1.13/user-guide.html#standalone-mode http://stackoverflow.com/questions/21769470/file-upload-using-java-websocket-api-and-javascript http://docs.oracle.com/javase/7/docs/api/javax/print/package-summary.html https://java.net/projects/websocket-spec

Github: https://github.com/ghwrivas/server-printer-ws