Search code examples
jsfprimefaceshttp-status-code-404graphicimagetagfile

Display a BLOB image using p:graphicImage in a tagfile


Displaying a BLOB image using <p:graphicImage> as follows.

<p:graphicImage value="#{categoryBean.image}">
    <f:param name="id" value="7"/>
</p:graphicImage>

Where CategoryBean has been defined as follows.

@Named
@ApplicationScoped
public class CategoryBean {

    @Inject
    private CategoryService service;

    public CategoryBean() {}

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

        if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
            return new DefaultStreamedContent();
        } else {
            String id = context.getExternalContext().getRequestParameterMap().get("id");
            byte[] bytes = Utils.isNumber(id) ? service.findImageById(Long.parseLong(id)) : null;
            return bytes == null ? null : new DefaultStreamedContent(new ByteArrayInputStream(bytes));
        }
    }
}

Regarding the above approach, the following custom tag should work flawlessly but it fails to display the image on <p:graphicImage> with no error / exception.

<my:image bean="#{categoryBean}" property="image" paramName="id" paramValue="7"/>

The tag file is located under /WEB-INF/tags/image.xhtml.

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:p="http://primefaces.org/ui"
                xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
                xmlns:f="http://xmlns.jcp.org/jsf/core">

    <p:graphicImage value="#{bean[property]}">
        <f:param name="#{paramName}" value="#{paramValue}"/>
    </p:graphicImage>
</ui:composition>

The generated <img> tag seems to look fine :

<img id="form:j_idt4"
     src="/ContextPath/javax.faces.resource/dynamiccontent.properties.xhtml?ln=primefaces&amp;v=5.3&amp;pfdrid=IA0%2F7ZuBnGS%2BSzeb%2BHyPOTo4Pxp4hjI6&amp;pfdrt=sc&amp;id=7&amp;pfdrid_c=true"
     alt=""/>

It only returns a HTTP 404 error.

Is there any flaw in the definition of the custom tag given?


Solution

  • It's caused by the way how PrimeFaces <p:graphicImage> identifies image requests. Basically, it converts the exact value expression #{bean[property]} to string, encrypts it and then passes it as pfdrid value. When the webbrowser needs to download the image by a brand new HTTP request, that value expression is decrypted and evaluated in the "current" EL context. However, during that moment, there's nowhere a #{bean} nor #{property} available anywhere in the EL context because there's no means of a JSF view with tagfiles and all. Only request, session and application scoped beans are available in EL context.

    There's nothing to do against this other than reporting an issue at PrimeFaces guys.

    As to alternate solutions, OmniFaces <o:graphicImage> does a better job in this by inspecting the target bean/method during render response already instead of during streaming the image. It immediately inspects #{bean[property]}, discovers that it actually represents #{categoryBean.image}, and then succeeds. Just to be sure I tested it in a tagfile like you have and it works fine for me whereas the PF one indeed fails as described.

    <o:graphicImage value="#{bean[property](paramValue)}" />
    

    public byte[] getImage(Long id) throws IOException {
        return service.findImageById(id);
    }