XPages: The Outputstream and binary data

As wiritten in the comments, Verne is correct. I just missed the „facesContext.responseComplete()“ in the beforeRenderResponse event.

If you want to get control over the outputstream of a XPage, you can use the response object from the ExternalContext:

var response = facesContext.getExternalContext().getResponse()

This will give you access to an object of type com.ibm.xsp.webapp.XspHttpServletResponse which allows some basic operations, but the response will always be encoded to UTF-8. You can not return any binary data directly.

But if you access the underlying LCDAdapterHttpServletResponse directly, it is possible to get the full control for the outputstream.

var exCon = facesContext.getExternalContext();
var response = exCon.getResponse().getDelegate();

This allows you to send any data you want to the browser. Here is an example for a JPEG rendered directly in the XPage:

1. The XPage

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" rendered="true">

   <xp:this.afterRenderResponse>
      <![CDATA[#{javascript:
         importPackage( ch.hasselba.xpages.demo );
         var exCon = facesContext.getExternalContext();
         var response = exCon.getResponse().getDelegate();
         ch.hasselba.xpages.demo.JPEGGenerator.generate( response );
      }]]>
   </xp:this.afterRenderResponse>

</xp:view>

 

2. The Java object to generate a JPEG

package ch.hasselba.xpages.demo;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import com.sun.image.codec.jpeg.ImageFormatException;
import com.sun.image.codec.jpeg.JPEGCodec;
import java.awt.Color;
import java.awt.Font;

/**
 * JPEG Generator for a simple demonstration of controlling the
 * HttpServletResponse output
 * 
 * @author Sven Hasselbach
 * @category Demo
 * @category Servlet
 * @category Image
 * @version 1.0
 */

public class JPEGGenerator {

    private static final long serialVersionUID = 1L;
    private static final int WIDTH = 800;
    private static final int HEIGHT = 200;
    private static final Color BACKGROUND_COLOR = new Color(224, 224, 224);
    private static final Color COLOR = new Color(0, 0, 0);
    private static final Font FONT = new Font("Times New Roman", Font.BOLD, 46);

    /**
     * generates a JPEG image and sends the result to the outputstream of the
     * HttpServlet
     * 
     * @param response
     *            HttpServletResponse
     * @author Sven Hasselbach
     * @version 1.0
     */
    public static void generate(final HttpServletResponse response) {
        ServletOutputStream out = null;
        try {

            // set the content type for JPEG
            response.setContentType("image/jpg");

            // get the ouput stream
            out = response.getOutputStream();

            // generate a image to convert
            BufferedImage img = createImage();

            // convert image to jpeg and send to output
            JPEGCodec.createJPEGEncoder(out).encode(img);

        } catch (ImageFormatException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * creates a simple "Hello World" image
     * 
     * @return BufferedImage
     * @author Sven Hasselbach
     * @version 1.0
     */
    public static BufferedImage createImage() {
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT,
                BufferedImage.TYPE_BYTE_INDEXED);
        Graphics graphics = image.getGraphics();
        graphics.setColor(BACKGROUND_COLOR);
        graphics.fillRect(0, 0, image.getWidth(), image.getHeight());
        graphics.setColor(COLOR);
        graphics.setFont(FONT);
        graphics.drawString("Hello World!", 10, HEIGHT / 2);

        return image;
    }

}

There is no need for splitting this example in a separated Java class. You can directly access the output in SSJS and send your binary data directly via write method of the ServletOutputStream.

Dieser Beitrag wurde unter Java, Server, ServerSide JavaScript, Web, XPages abgelegt und mit , , , , , , , verschlagwortet. Setze ein Lesezeichen auf den Permalink.

7 Antworten zu XPages: The Outputstream and binary data

  1. Hmm,
    I thought writers have encoding while streams are by nature binary. I used PDFBox, FOP and ODFToolkit to send binary to the response.getOutputstream without issues.

    • This only applies for normal cases, not for the modified version. The XspHttpServletResponse uses internally a buffer/block-mechanism for a better performance, and that’s why you can not write f.e. a JPEG file directly.

      It works for PDF’s, but if you use my example above and just do the same with the default OutputStream, it will fail. Browsers can not handle the generated image:


      var exCon = facesContext.getExternalContext();
      var response = exCon.getResponse();
      ch.hasselba.xpages.demo.JPEGGenerator.generate( response );

      Even if you write directly some bytes to the OutputStream this will not work.

  2. Christian sagt:

    Hallo,
    Ich nutze diese Technik, leicht verändert, auf einem Button um zur laufzeit erzeugt PDFs an den benutzer zum Download zurück zu geben. Leider habe ich bemerkt das
    nach dem Download die gesamte Seite für ca 30-40 sekunden nicht reagiert.
    Hast du da zufällig eine Idee um das Problem zu lösen?

    • Christian sagt:

      Ausführlich mache ich folgendes wobei das Problem auftritt:

      public void exportPdfOutputStream(TemplateData pdfData, String templateFile, String filename) {

      ExternalContext con = facescontext.getExternalContext();
      XspHttpServletResponse response = (XspHttpServletResponse) con.getResponse();

      try {
      ServletOutputStream writer = response.getOutputStream();

      // setting response headers for browser
      response.setContentType(„application/pdf“);
      response.setHeader(„Cache-Control“, „no-cache“);
      response.setDateHeader(„Expires“, -1);
      response.setHeader(„Content-Disposition“, „attachment; filename=\““ + filename + „\““);

      PdfReader pdfTemplate = getPdfReader(templateFile);

      PdfStamper stamper = new PdfStamper(pdfTemplate, writer);

      stamper.setFormFlattening(true);

      setDataToPdfDocument(pdfData, stamper);

      stamper.close();
      pdfTemplate.close();
      writer.flush();
      writer.close();
      facescontext.responseComplete();
      } catch (Exception e) {
      String errorMessage = „PDF Exporter: An error occureed: “ + e.toString();
      try {
      response.sendError(500, errorMessage);
      } catch (Exception e2) {
      e2.printStackTrace();
      }

      }
      }

      • Ich denke mal es hat mit der SubmitLatency zu tun, die man im CSJS mit XSP.allowSubmit() wieder zurück setzen kann. Jetzt stellt sich nur die Frage, wie man den CSJS Code ausführen kann…

        • Um den CSJS Code auszuführen, kann man mit einem Cookie arbeiten, der in der HTTP Response untergebracht wird. Mit einem Interval läßt sich die Existenz abfragen und dann das XSP.allowSubmit() ausführen. Ich poste heute Abend mal den Code dazu.

  3. Pingback: XPages: File downloads and blocked UI | blog@hasselba.ch

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.