如何在 p:dataTable 中使用带有 StreamedContent 的 p:graphicImage?

14
我希望能够在PrimeFaces数据表中从数据库动态加载图像。 代码如下,基于此PF论坛主题: ```html ``` 请注意,在value属性中使用了EL表达式来获取图像的ID并将其传递给后台bean的getImage方法。
<p:dataTable id="tablaInventario" var="inv" value="#{registrarPedidoController.inventarioList}" paginator="true" rows="10"
    selection="#{registrarPedidoController.inventarioSelected}" selectionMode="single"                                     
    update="tablaInventario tablaDetalle total totalDesc" dblClickSelect="false" paginatorPosition="bottom">
    <p:column sortBy="producto.codigo" filterBy="producto.codigo">
        <f:facet name="header">#{msg.codigo}</f:facet>
        #{inv.producto.codProducto}
    </p:column>                            
    <p:column>
        <f:facet name="header">Foto</f:facet>
        <p:graphicImage id="photo" value="#{registrarPedidoController.streamedImageById}" cache="FALSE">
            <f:param name="inv" value="#{inv.id}" />
        </p:graphicImage>                                
    </p:column>
</p:dataTable>

使用

public StreamedContent getStreamedImageById() {
    DefaultStreamedContent image = null;
    String get = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("inv");
    System.out.println("[Param]: " + get); // This prints null.
    Long id = new Long(get);
    List<Inventario> listInventarios = controladorRegistrarPedido.listInventarios();

    for (Inventario i : listInventarios) {
        if (i.getId().compareTo(id) == 0) {
            byte[] foto = i.getProducto().getFoto();
            image = new DefaultStreamedContent(new ByteArrayInputStream(foto), "image/png");
        }
    }

    return image;
}

然而,我无法使其工作。我的参数将“null”传递给我的后端bean。这是如何引起的,我该如何解决?
我正在使用Netbeans 6.9.1,JSF 2.0和Primefaces 2.2.RC2。
我首先尝试了BalusC的解决方案,它可以正常工作,但UI中未呈现图像。 Glassfish抛出以下异常:
WARNING: StandardWrapperValve[Faces Servlet]: PWC1406: Servlet.service() for servlet Faces Servlet threw exception
java.lang.NullPointerException
        at com.sun.faces.mgbean.BeanManager$ScopeManager$ViewScopeHandler.isInScope(BeanManager.java:552)

感谢BalusC的帮助,我终于能够工作了。为了管理getStreamedImageId,我必须使用RequestScoped、SessionScoped或ApplicationScoped。然而,在UI中总是设置默认图像(对于空值情况),而不是与每一行相对应的预期图像。新代码如下:

public StreamedContent streamedById(Long id) {
    DefaultStreamedContent image = null;

    System.out.println("[ID inventario]: " + id);

    List<Inventario> listInventarios = controladorRegistrarPedido.listInventarios();
    for (Inventario i : listInventarios) {
        if (i.getId().equals(id)) {
            byte[] foto = i.getProducto().getFoto();
            if (foto != null) {
                System.out.println("   [Foto]: " + foto);
                image = new DefaultStreamedContent(new ByteArrayInputStream(foto), "image/png");
                break;
            }
        }


    }
    if (image == null) {
        System.out.println("       [Image null]");
        byte[] foto = listInventarios.get(0).getProducto().getFoto();
        image = new DefaultStreamedContent(new ByteArrayInputStream(foto), "image/png");
    }

    System.out.println("   [Foto Streamed]: " + image);

    return image;

}
3个回答

35

<p:graphicImage>将调用getter方法两次。第一次是在渲染<img>元素以生成HTML时,需要在src属性中设置URL。如果您只返回new DefaultStreamedContent(),则它将自动生成正确的src属性中的URL。第二次是浏览器真正请求图像的时候,这就是您应该返回实际图像的时刻。

因此,getter方法应该基本上看起来像这样:

public StreamedContent getStreamedImageById() {
    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. Get ID value from actual request param.
        String id = context.getExternalContext().getRequestParameterMap().get("id");
        Image image = service.find(Long.valueOf(id));
        return new DefaultStreamedContent(new ByteArrayInputStream(image.getBytes()));
    }
}

3
BalusC的答案非常出色,质量很高。我选择了第一个解决方案,它适用于长参数,并且我从数据库获取到了图片,但是它没有在UI中呈现出来,我检查了Glassfish日志并发现了一些异常,有任何想法吗?(我已经将它添加到我的帖子中。) - Santi Agüero
2
我会从分享异常开始,因为它们通常已经是完整答案了。类型、消息和堆栈跟踪的第一行通常足够提供信息。 - BalusC
4
用于提供 p:graphicImage 内容的 bean 需要是请求作用域、会话作用域(如果本身是无状态的,甚至可以是应用程序作用域)。视图作用域不被支持。您需要将视图作用域 bean 中的方法分离到适当作用域的单独 bean 中。这也有额外的好处,即更好地可重用于其他地方。 - BalusC
1
是的!这是一个好观点,我认为我几乎准备好了。现在没有抛出异常,但是图像没有被渲染(但是如果我在UI中将硬编码1放入registrarPedidoController.getStreamedImageById(1),它可以工作(当然带来相同的图像)。我看到我的日志,有一个奇怪的情况,有一个点它正在寻找id等于0(当我在我的数据库中没有它);我应该处理这些情况或空图像吗?在getStreamedImageById方法中,对于这些情况我应该返回什么? - Santi Agüero
@Balusc 在这行代码中,service是什么? Image image = service.find(Long.valueOf(id)); - subhakar patnala
显示剩余12条评论

3
如果我们通过Hibernate保存图像,则将图像保存在数据库中作为byte[]。我使用<p:fileUpload...标签上传图像,然后使用Hibernate将图像与其他数据值一起保存。
在第二个页面上,我使用整个表格数据(包括图像)进行显示。
<p:dataTable  var="data" value="#{three.all}" ....

以及使用动态图片

<p:graphicImage   alt="image"  value="#{three.getImage(data)}" cache="false"  >
                <f:param id="image_id" name="image_id" value="#{data.number}" />
</p:graphicImage></p:dataTable>

这里的“three”是Backing Bean的名称。在getAll()方法中,我通过Hibernate从表中检索数据,在同一个方法中,我创建了一个HashMap<Integer, byte[]>。HashMap是bean的实例变量,该Bean是SessionScoped。我将images(以byte[]形式存在)与整数image_id一起放置。

代码:

    for (int i=0; i<utlst.size(); i++ ){
             images.put(utlst.get(i).getNumber(), utlst.get(i).getImage());}
//utlst is the object retrieved from database. number is user-id.

在视图getImage.xhtml中,<p:graphicImage alt="image" value="#{three.getImage(data)}" cache="false" >调用了方法getImage(data /*我正在传递当前正在迭代的列表对象*/ )
getImage的代码:
public StreamedContent getImage(Util ut) throws IOException {
       //Util is the pojo

          String image_id = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("image_id");
          System.out.println("image_id: " + image_id);

          if (image_id == null) {

                defaultImage=new DefaultStreamedContent(FacesContext.getCurrentInstance().getExternalContext().getResourceAsStream("/Capture.PNG"), "image/png");

            return defaultImage; 
        }


         image= new DefaultStreamedContent(new ByteArrayInputStream(images.get(Integer.valueOf(image_id))), "image/png");

        return image;
    }

只需在会话中使用HashMap将具有ID的动态图像保存起来,它们就可以被正确地流式传输。

谢谢 & 祝好, Zeeshan


2
在PrimeFaces 3.2中,这个bug仍然存在。我将使用解决方法:
<p:graphicImage value="#{company.charting}">
    <f:param id="a" name="a" value="#{cc.attrs.a}" />
    <f:param id="b" name="b" value="#{cc.attrs.b}" />
</p:graphicImage>

并且

ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
String a= externalContext.getRequestParameterMap().get("a");
String b= externalContext.getRequestParameterMap().get("b");

即使如此,豆子仍被调用了2次。但在第二次调用中,变量a + b被填充了;-)

该死的bug


谢谢,这很有用。在其中一个请求中,参数为空,在另一个请求中则已填充,因此只需要一个属性就足够了。只需在开始时检查参数不为空即可。 - damian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接