Your concrete problem is caused because the webbrowser is actually downloading the PDF file in a physically completely separate HTTP request than the HTTP request which is generating and sending the HTML output based on JSF source code. You probably already know that view scoped beans are tied to a particular JSF view via javax.faces.ViewState
hidden input field. If this get changed or is absent, then the request gets a new and different view scoped bean instance.
In other words, while the browser is downloading the PDF file from the server in a separate HTTP request, it isn't using the same @ViewScoped
bean instance, but instead getting a brand new and completely independent instance which does not hold the same properties (state) as the one tied to the page and thus the whole StreamedContent
is simply null
at that point.
This problem with <p:media>
and StreamedContent
has essentially the same grounds as the problem with <p:graphicImage>
and StreamedContent
which is answered several times before:
- Display dynamic image from database with p:graphicImage and StreamedContent
- Display database blob images in <p:graphicImage> inside <ui:repeat>
- How to use p:graphicImage with StreamedContent within p:dataTable?
In your particular case, you need to redesign the whole bunch in such way that the DocumentsBean
backing bean stores the PDF file in some place (e.g. temp disk, server memory, database, etc) by an unique identifier and then pass exactly that unique identifier along as request parameter to <p:media>
as follows:
<p:media value="#{mediaManager.stream}" width="100%" height="700px" player="pdf">
<f:param name="id" value="#{documentsBean.mediaId}" />
</p:media>
Whereby the MediaManager
backing bean look something like this:
@ManagedBean
@ApplicationScoped
public class MediaManager {
@EJB
private MediaService service;
public StreamedContent getStream() throws IOException {
FacesContext context = FacesContext.getCurrentInstance();
if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
// So, we're rendering the HTML. Return a stub StreamedContent so that it will generate right URL.
return new DefaultStreamedContent();
} else {
// So, browser is requesting the media. Return a real StreamedContent with the media bytes.
String id = context.getExternalContext().getRequestParameterMap().get("id");
Media media = service.find(Long.valueOf(id));
return new DefaultStreamedContent(new ByteArrayInputStream(media.getBytes()));
}
}
}