Primefaces P: Media Pdf Not Loading

When I try to load my page including a primefaces media pdf the PDF is not loaded. I generate the PDF in my postconstruct and keep the streamedcontent in a seperate variable. In my JSF I call the getStream method that returns the streamedcontent.

JSF page:

<html xmlns=""
      xmlns:ui=""
      xmlns:h=""
      xmlns:c=""
      xmlns:p=""
      xmlns:f="">
<ui:composition template="/templates/header.xhtml">
    <ui:define name="content">
        <f:metadata>
            <f:viewParam name="invoiceID" value="#{invoiceBean.invoiceID}"/>
        </f:metadata>
        <ui:param name="invoiceID" value="#{invoiceBean.invoiceID}"/>
        <h4 style="text-align: center;"><h:outputText
                value="#{msgs['invoice.thankYou']}"/></h4>
        <div class="card">
            <p:media value="#{invoiceBean.stream}" player="pdf" width="100%" height="800px">
                Your browser can't display pdf,
                <h:outputLink
                        value="#{invoiceBean.streamedContent}">click
                </h:outputLink>
                to download pdf instead.
            </p:media>
        </div>
    </ui:define>
</ui:composition>
</html>

Bean:

@Model
@Getter
@Setter
public class InvoiceBean {
    @Inject
    InvoiceService invoiceService;
    @Inject
    HttpServletRequest httpServletRequest;
    private Invoice invoice;
    private String invoiceID;
    private StreamedContent streamedContent;

    @PostConstruct
    public void initInvoice() {
        User user = getLoggedInUser();
        invoiceID = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("invoiceID");
        invoice = invoiceService.getInvoice(Long.parseLong(invoiceID));
        PDFGenerator pdf = new PDFGenerator(invoice);
        streamedContent = pdf.getStreamedContent();
    }

    public StreamedContent getStream() throws IOException{
        FacesContext context = FacesContext.getCurrentInstance();

        if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
            return new DefaultStreamedContent();
        } else {
            return streamedContent;
        }
    }
}
8

1 Answer

Because the stream is dynamic, this post from @BalusC is very useful to resolve this issue. In particular "never declare StreamedContent nor any InputStream or even UploadedFile as a bean property; only create it brand-new in the getter of a stateless @ApplicationScoped bean when the webbrowser actually requests the image content."

The p:media tag needs to disable the cache.

                <p:media value="#{pdfViewController.pdf}" player="pdf" cache="false"
                    width="100%" height="500px" >
                    Your browser can't display pdf.
                </p:media>
                <br/>
                <h:form>
                    <p:commandButton value="Download" icon="pi pi-arrow-down" ajax="false">
                        <p:fileDownload value="#{pdfViewController.pdfDownload}" />
                    </p:commandButton>
                </h:form>

The backing bean needs to do all the work in the getter, not in the PostConstruct method. (See the comments in @BalusC's post about this.) I was able to use both Request (per Showcase) and Session beans, but the PrimeFaces documentation warns against ViewScoped.

@Named
@RequestScoped
public class PdfViewController implements java.io.Serializable {
    public StreamedContent getPdf() {
        return DefaultStreamedContent.builder()
                .contentType("application/pdf")
                .stream(() -> makePDFStream())
                .build();
    }

    protected ByteArrayInputStream makePDFStream() {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        String dt = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss-n"));
        makePDF(baos, "This message was created at " + dt);
        return new ByteArrayInputStream(baos.toByteArray());
    }

    // use iText to create a PDF document "on the fly"
    protected void makePDF(OutputStream os, String message) {
        PdfDocument pdf = new PdfDocument(new PdfWriter(os));
        try (Document document = new Document(pdf)) {
            String line = "Hello! Welcome to iTextPdf";
            document.add(new Paragraph(line));
            document.add(new LineSeparator((new SolidLine())));
            PdfFont font = PdfFontFactory.createFont(StandardFonts.TIMES_ITALIC);
            Text msgText = new Text(message).setFont(font).setFontSize(10);
            document.add(new Paragraph().add(msgText));
        } catch (IOException ex) {
            LOG.log(Level.SEVERE, "PDF document creation error", ex);
        }
        // os is automatically written and closed when document is autoclosed
    }
}

In order to support the download option and because the bean is @request, the stream is recreated when needed. It was also necessary to include the .name("...)", whereas the p:media value= tag failed if name is specified, hence it needs a separate method.

    public StreamedContent getPdfDownload() {
        return DefaultStreamedContent.builder()
                .name("temp.pdf")
                .contentType("application/pdf")
                .stream(() -> makePDFStream())
                .build();
    }

Tested with PrimeFaces v8 and Wildfly v21 (w/Mojarra).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Chloe Bennett

Chloe Bennett

Culture, Media & Entertainment Columnist

Chloe Bennett explores the intersection of pop culture, streaming entertainment, digital trends, and contemporary lifestyle. Her weekly commentary reaches thousands of culture enthusiasts.

Share this article
Twitter Facebook Pinterest