使用p:graphicImage和StreamedContent从数据库显示动态映像 [英] Display dynamic image from database with p:graphicImage and StreamedContent

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

问题描述

我试图显示保存在数据库中的 StreamedContent < p:graphicImage> 如下:

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?

stdout输出以下内容:

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


推荐答案

< p:graphicImage> 需要一个特殊的getter方法。

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

第一个请求JSF页面的HTML结果的HTTP请求将会在每个生成的图像中调用两次,第一次调用getter以便在 src 中生成具有右侧唯一且自动生成的URL的HTML < img> / code>属性,它包含关于哪个bean和getter应该在webbrowser要请求图像时被调用的信息。请注意,getter在此时需要返回图片的内容。它不会以任何方式使用,因为这不是HTML的工作原理(图像在HTML输出中不是内联,而是单独请求它们)。

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).

webbrowser检索HTML结果作为HTTP响应,它将解析HTML源,以便将结果可视地呈现给最终用户。一旦webbrowser在解析HTML源期间遇到< img> 元素,那么它将在URL中发送一个全新的HTTP请求,如其 src 属性,以便下载该图像的内容并将其嵌入到视觉呈现中。这将第二次调用getter方法,反过来应该返回实际图像内容。

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.

在您的特定情况下 PrimeFaces显然无法识别和调用getter以检索实际的图片内容,不返回预期的图像内容。使用#{item} 变量名称和日志中的调用次数表明您在< ui:repeat> < h:dataTable> 。最可能的是,支持bean是请求作用域,并且在请求映像期间数据模型未正确保留,并且JSF将不能在右循环期间调用getter。视图范围的bean也不会工作,因为当浏览器实际请求图像时,JSF视图状态不可用。

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.

为了解决这个问题,你最好的办法是重写getter方法,这样它可以在每个请求的基础上被调用,其中你传递的唯一的图像标识符为< f:param> ,而不是依赖一些可能在后续HTTP请求期间不同步的支持bean属性。使用一个单独的应用程序作用域管理bean对于没有任何状态的这将是完全有意义的。此外, InputStream 只能读取一次,而不能多次。

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.

换句话说: never声明 StreamedContent ,也不要将任何 InputStream 或甚至 UploadedFile 属性;只有当webbrowser实际请求图像内容时,才在无状态 @ApplicationScoped bean的getter中创建它全新。

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.

Eg

<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 这个:

@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调用getter多次。不要将此特殊情况作为其他标准(非特殊)情况的借口。还请注意,您不能使用传递方法参数的EL 2.2功能,例如#{studentImages.image(student.id)} ,因为此参数不会最终在图片网址中。因此,你真的需要传递他们< f:param>

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>.

如果您碰巧使用 OmniFaces 2.0或更高版本,请考虑使用其 < o:graphicImage> ,可以更直观地使用应用程序范围的getter方法直接委托给服务方法和支持EL 2.2方法参数。

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.

因此:

<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();
    }

}

另请参阅关于主题的博客

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

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