Display database blob images in <p:graphicImage> inside <ui:repeat>
Asked Answered
K

6

34

I'm using PrimeFaces 3.2 on JBoss 7.1.1.

I am trying to display an image which is stored in a BLOB in a MySQL database in <ui:repeat>. The image is stored in a byte[] and then converted to a StreamedContent as follows:

InputStream stream = new ByteArrayInputStream(ingredient.getImage());
ingredient.setJsfImage(new DefaultStreamedContent(stream, "image/jpg"));

Then I am trying to display it in a Facelet as follows:

<ui:repeat var="ingredient" value="#{formBean.ingredientResultSet}">
    <p:panel id="resultsPanel" header="#{ingredient.location.shopName}">
        <p:graphicImage value="#{ingredient.jsfImage}" alt="No picture set" />
...

However, when loading the page, I get the following error in JBoss:

SEVERE [org.primefaces.application.PrimeResourceHandler] (http--127.0.0.1-8080-12) Error in streaming dynamic resource.

How is this caused and how can I solve it?

Kalamazoo answered 9/4, 2012 at 13:26 Comment(0)
S
57

You need to realize that the <p:graphicImage> actually renders a <img src> element with just an URL which is then later individually invoked by the webbrowser when it's about to parse the obtained HTML markup and present the results.

So, whatever you do in the getter method of <p:graphicImage> it must be designed that way that it can be invoked on a per-request basis. So, the most sane approach would be to create a <p:graphicImage> with a <f:param> wherein the <p:graphicImage value> points an entirely standalone request or application scoped bean (and thus absolutely not view or session scoped), and the <f:param value> points the unique image identifier.

E.g.

<p:graphicImage value="#{images.image}">
    <f:param name="id" value="#{someBean.imageId}" />
</p:graphicImage>

Where the Images backing bean can look like this:

@Named
@ApplicationScoped
public class Images {

    @EJB
    private ImageService service;

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

        if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
            // So, we're rendering the view. Return a stub StreamedContent so that it will generate right URL.
            return new DefaultStreamedContent();
        }
        else {
            // So, browser is requesting the image. Return a real StreamedContent with the image bytes.
            String id = context.getExternalContext().getRequestParameterMap().get("id");
            Image image = service.find(Long.valueOf(id));
            return new DefaultStreamedContent(new ByteArrayInputStream(image.getBytes()));
        }
    }

}

Or, if you're already using OmniFaces 2.0 or newer, then consider using its <o:graphicImage> instead which can be used more intuitively, almost exectly the way as you expected. See also the blog on the subject.

See also:

Siebert answered 15/4, 2012 at 12:7 Comment(2)
Could you show, how the ImageService should look like?Shore
@John: it's just your service/DAO class which which returns your Image entity. How it is doing its job (JPA, JDBC, Hibernate, whatever) is not of interest for JSF.Siebert
Z
3

I do not understand why do you need this. You can just create a servlet that will get the image from database with a mapping "/images/yourimage.jpg".

Here is a quick tutorial with a corresponding code in it on how to do this.

JSF - Displaying images from database in JSF

Zajac answered 17/4, 2012 at 14:10 Comment(1)
Yes, that is smart solution, in particular for SEO solutions.Escarp
R
3

Thanks BalusC. I did this thing by defining an integer in backing bean and assigning a diffent value to it while updating the ByteArray for image. This integer serves as unique identifier for image. Then I set this integer as value of <f:param> within <p:graphicImage> on page.

image tag look like

 <p:graphicImage value="#{empBean.image}">
    <f:param name="id" value="#{empBean.imageId}" />
  </p:graphicImage>`

and setting image in backing bean as

 if(user.getPicture()!=null){
        imageId = (int) new Date().getTime();
        BinaryStreamImpl bs = new BinaryStreamImpl(user.getPicture());  
//picture is byte[] property in user class      
        this.image =   new DefaultStreamedContent(bs.getInputStream(), "image/png");
    }

Its now working good.

Richman answered 2/9, 2015 at 13:0 Comment(0)
I
1

You have to keep in mind what the graphicImage component renders to on the page. It renders to the HTML <img> element. In the browser, the request will be made for the JSF page, then subsequent requests will occur for each of the images on the page as well.

These images are coming from the PrimeFaces resource servlet and not FacesServlet, meaning that scope of the Managed Bean and its properties are probably not applicable.

Try loading the blob into a byte array first then the this will probably start working.

EDIT: See my following answer to this similar question. graphicimage not rendering streamedcontent in Primefaces

Icecold answered 9/4, 2012 at 15:32 Comment(1)
Thanks for the answer, but as per my description above, the image is already a byte[]Kalamazoo
G
0
InputStream is = new ByteArrayInputStream((byte[]) yourBlobData);
myImage = new DefaultStreamedContent(is, "image/png");

in jsf page;

<p:graphicImage value="#{controller.myImage}" style="width:200px;width:500px" />
Groundling answered 19/2, 2014 at 9:15 Comment(0)
R
-2

mmm I 99% sure that <p:graphicImage doesnt work with ui:repeat you ll have lot of problems with this (Because I did XD), I recommend to you to create a servlet and serve the images as an inputstream (there are a lot of examples out there).

Roughcast answered 14/4, 2012 at 18:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.