使用p:graphicImage和StreamedContent显示来自数据库或远程源的动态图像 [英] Display dynamic image from database or remote source with p:graphicImage and StreamedContent

查看:111
本文介绍了使用p:graphicImage和StreamedContent显示来自数据库或远程源的动态图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试显示图像字节,该图像字节在<p:graphicImage>中以StreamedContent的形式保存在数据库中,如下所示:

 <p:graphicImage  value="#{item.imageF}" width="50"  id="grpImage" height="80"/>
 

 private StreamedContent content; // getter and setter

public StreamedContent getImageF() {

    if (student.getImage() != null) {
        InputStream is = new ByteArrayInputStream(student.getImage());
        System.out.println("Byte :"+student.getImage());
        content = new DefaultStreamedContent(is, "", student.getStuID());
        System.out.println("ddd ------------------------------- " + content);
        return content;
    }

    return content;
}
 

这将返回空白图像.这是怎么引起的,我该如何解决?

标准输出显示以下内容:

INFO: Byte :[B@a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@b0887b
INFO: Byte :[B@a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1d06a92
INFO: Byte :[B@d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@39a60
INFO: Byte :[B@d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@8c3daa
INFO: Byte :[B@124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1dbe05b
INFO: Byte :[B@124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@66a266
INFO: Byte :[B@a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1293976
INFO: Byte :[B@a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@17b7399
INFO: Byte :[B@d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1e245a5
INFO: Byte :[B@d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@4a7153
INFO: Byte :[B@124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1561bfd
INFO: Byte :[B@124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@47a8c2

解决方案

<p:graphicImage>需要特殊的getter方法.也就是说,每个生成的图像都会被调用两次,每个图像都在完全不同的HTTP请求中被调用.

已请求JSF页面的HTML结果的第一个HTTP请求将首次调用getter,以生成HTML <img>元素,该元素在属性,其中包含有关在Web浏览器要请求图像时应确切调用哪个bean和getter的信息.请注意,此时吸气剂确实不需要需要返回图像的内容.不会以任何方式使用它,因为那不是HTML的工作方式(图像不会在HTML输出中内联",而是单独请求).

一旦Web浏览器检索到HTML结果作为HTTP响应,它将解析HTML源,以便将结果可视地呈现给最终用户.一旦网络浏览器在解析HTML源代码期间遇到<img>元素,则它将在src属性中指定的URL上发送全新的HTTP请求,以便下载该图像的内容并将其嵌入到可视文件中介绍.这将第二次调用getter方法,该方法应返回 actual 图像内容.

在您的特定情况下 PrimeFaces显然无法识别并调用吸气剂以检索实际图像内容,或者吸气剂未返回预期的图像内容. #{item}变量名的用法和日志中的大量调用表明您在<ui:repeat><h:dataTable>中使用它.支持bean很有可能是请求范围的,并且在对图像的请求期间未正确保留数据模型,并且JSF将无法在正确的迭代回合中调用getter.由于在浏览器实际请求图像时JSF视图状态无处可用,因此视图范围的bean也将不起作用.


要解决此问题,最好的选择是这样重写getter方法,以便可以在每个请求的基础上调用该方法,在此方法中,您将唯一的图像标识符作为<f:param>传递,而不是依赖于某些后备bean属性,这些属性在后续的HTTP请求期间可能会不同步".为此使用一个单独的应用程序范围的托管Bean完全有意义,因为它没有任何状态.此外,InputStream只能读取一次,不能多次读取.

换句话说:不要将StreamedContent或任何InputStream甚至UploadedFile都声明为bean属性;仅当网络浏览器实际请求图像内容时,才在无状态@ApplicationScoped Bean的吸气剂中全新创建它.

例如

 <p:dataTable value="#{bean.students}" var="student">
    <p:column>
        <p:graphicImage value="#{studentImages.image}">
            <f:param name="studentId" value="#{student.id}" />
        </p:graphicImage>
    </p:column>
</p:dataTable>
 

StudentImages支持bean的外观如下:

 @Named // Or @ManagedBean
@ApplicationScoped
public class StudentImages {

    @EJB
    private StudentService service;

    public StreamedContent getImage() 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 image. Return a real StreamedContent with the image bytes.
            String studentId = context.getExternalContext().getRequestParameterMap().get("studentId");
            Student student = studentService.find(Long.valueOf(studentId));
            return new DefaultStreamedContent(new ByteArrayInputStream(student.getImage()));
        }
    }

}
 

请注意,这是一个非常特殊的情况,其中考虑到<p:graphicImage>的工作原理,完全合法地使用getter方法执行业务逻辑.即通常不赞成在getter中调用业务逻辑,另请参见为什么JSF调用吸气剂多次.不要将此特殊情况用作其他标准(非特殊)情况的借口.另请注意,您不能使用像这样传递方法参数的EL 2.2功能,例如#{studentImages.image(student.id)},因为该参数不会以图像URL结尾.因此,您确实需要将它们作为<f:param>传递.


如果您碰巧使用了 OmniFaces 2.0或更高版本,请考虑使用其 <p:dataTable value="#{bean.students}" var="student"> <p:column> <o:graphicImage value="#{studentImages.getImage(student.id)}" /> </p:column> </p:dataTable>

使用

 @Named // Or @ManagedBean
@ApplicationScoped
public class StudentImages {

    @EJB
    private StudentService service;

    public byte[] getImage(Long studentId) {
        return studentService.find(studentId).getImage();
    }

}
 

另请参见博客.

>

I'm trying to display image bytes which is saved in database as a StreamedContent in the <p:graphicImage> as follows:

<p:graphicImage  value="#{item.imageF}" width="50"  id="grpImage" height="80"/>

private StreamedContent content; // getter and setter

public StreamedContent getImageF() {

    if (student.getImage() != null) {
        InputStream is = new ByteArrayInputStream(student.getImage());
        System.out.println("Byte :"+student.getImage());
        content = new DefaultStreamedContent(is, "", student.getStuID());
        System.out.println("ddd ------------------------------- " + content);
        return content;
    }

    return content;
}

This returns a blank image. How is this caused and how can I solve it?

The stdout prints the following:

INFO: Byte :[B@a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@b0887b
INFO: Byte :[B@a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1d06a92
INFO: Byte :[B@d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@39a60
INFO: Byte :[B@d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@8c3daa
INFO: Byte :[B@124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1dbe05b
INFO: Byte :[B@124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@66a266
INFO: Byte :[B@a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1293976
INFO: Byte :[B@a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@17b7399
INFO: Byte :[B@d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1e245a5
INFO: Byte :[B@d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@4a7153
INFO: Byte :[B@124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1561bfd
INFO: Byte :[B@124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@47a8c2

解决方案

The <p:graphicImage> requires a special getter method. It will namely be invoked twice per generated image, each in a completely different HTTP request.

The first HTTP request, which has requested the HTML result of a JSF page, will invoke the getter for the first time in order to generate the HTML <img> element with the right unique and auto-generated URL in the src attribute which contains information about which bean and getter exactly should be invoked whenever the webbrowser is about to request the image. Note that the getter does at this moment not need to return the image's contents. It would not be used in any way as that's not how HTML works (images are not "inlined" in HTML output, but they are instead requested separately).

Once the webbrowser retrieves the HTML result as HTTP response, it will parse the HTML source in order to present the result visually to the enduser. Once the webbrowser encounters an <img> element during parsing the HTML source, then it will send a brand new HTTP request on the URL as specified in its src attribute in order to download the content of that image and embed it in the visual presentation. This will invoke the getter method for the second time which in turn should return the actual image content.

In your particular case PrimeFaces was apparently either unable to identify and invoke the getter in order to retrieve the actual image content, or the getter didn't return the expected image content. The usage of #{item} variable name and the lot of calls in the log suggests that you were using it in an <ui:repeat> or a <h:dataTable>. Most likely the backing bean is request scoped and the datamodel isn't properly preserved during the request for the image and JSF won't be able to invoke the getter during the right iteration round. A view scoped bean would also not work as the JSF view state is nowhere available when the browser actually requests the image.


To solve this problem, your best bet is to rewrite the getter method as such so that it can be invoked on a per-request basis wherein you pass the unique image identifier as a <f:param> instead of relying on some backing bean properties which may go "out of sync" during subsequent HTTP requests. It would make completely sense to use a separate application scoped managed bean for this which doesn't have any state. Moreover, an InputStream can be read only once, not multiple times.

In other words: 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.

E.g.

<p:dataTable value="#{bean.students}" var="student">
    <p:column>
        <p:graphicImage value="#{studentImages.image}">
            <f:param name="studentId" value="#{student.id}" />
        </p:graphicImage>
    </p:column>
</p:dataTable>

Where the StudentImages backing bean can look like this:

@Named // Or @ManagedBean
@ApplicationScoped
public class StudentImages {

    @EJB
    private StudentService service;

    public StreamedContent getImage() 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 image. Return a real StreamedContent with the image bytes.
            String studentId = context.getExternalContext().getRequestParameterMap().get("studentId");
            Student student = studentService.find(Long.valueOf(studentId));
            return new DefaultStreamedContent(new ByteArrayInputStream(student.getImage()));
        }
    }

}

Please note that this is a very special case wherein performing business logic in a getter method is completely legit, considering how the <p:graphicImage> works under the covers. Invoking business logic in getters is namely usually frowned upon, see also Why JSF calls getters multiple times. Don't use this special case as excuse for other standard (non-special) cases. Please also note that you can't make use of EL 2.2 feature of passing method arguments like so #{studentImages.image(student.id)} because this argument won't end up in the image URL. Thus you really need to pass them as <f:param>.


If you happen to use OmniFaces 2.0 or newer, then consider using its <o:graphicImage> instead which can be used more intuitively, with an application scoped getter method directly delegating to the service method and supporting EL 2.2 method arguments.

Thus so:

<p:dataTable value="#{bean.students}" var="student">
    <p:column>
        <o:graphicImage value="#{studentImages.getImage(student.id)}" />
    </p:column>
</p:dataTable>

With

@Named // Or @ManagedBean
@ApplicationScoped
public class StudentImages {

    @EJB
    private StudentService service;

    public byte[] getImage(Long studentId) {
        return studentService.find(studentId).getImage();
    }

}

See also the blog on the subject.

这篇关于使用p:graphicImage和StreamedContent显示来自数据库或远程源的动态图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆