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.
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.
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?
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.
Pingback: XPages: File downloads and blocked UI | blog@hasselba.ch