使用p:graphicImage和StreamedContent显示来自数据库的dynamic图像

我试图显示保存在数据库中的图像字节为<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,以便在包含信息的src属性中生成具有正确唯一且自动生成的URL的HTML <img>元素当web浏览器即将请求图像时,应该确切地调用哪个bean和getter。 请注意,此时的获取者不需要返回图片的内容。 它不会以任何方式使用,因为这不是HTML的工作方式(图像不是在HTML输出中“内联”的,而是单独请求的)。

一旦web浏览器将HTML结果作为HTTP响应来检索,它将parsingHTML源以便以可视方式向最终用户呈现结果。 一旦网页浏览器在parsingHTML源文件时遇到<img>元素,就会在其src属性中指定的URL上发送全新的HTTP请求,以便下载该图像的内容并将其embedded到可视化表示中。 这将第二次调用getter方法,反过来应该返回实际的图像内容。

你的特殊情况下, PrimeFaces显然无法识别和调用获取器来获取实际的图像内容,或者获取器没有返回期望的图像内容。 #{item}variables名称和日志中调用的大量使用表明您正在使用它在<ui:repeat><h:dataTable> 。 支持bean最有可能是请求范围,在请求图像时数据模型没有正确保存,JSF将无法在正确的迭代过程中调用getter。 视图范围的bean也不能工作,因为当浏览器实际请求图像时,JSF视图状态无处可用。


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

换句话说: 从不声明StreamedContent也不将任何InputStream或者UploadedFile为bean属性; 当webbrowser实际请求图像内容时,只在无状态的@ApplicationScoped bean的getter中创build它

例如

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

请注意,这是一个非常特殊的情况,在getter方法中执行业务逻辑是完全合法的,考虑<p:graphicImage>如何在封面下工作。 在getter中调用业务逻辑通常是不被接受的,另请参见为什么JSF多次调用getters 。 不要将这个特殊情况作为其他标准(非特殊)情况的借口。 还请注意,你不能使用类似于#{studentImages.image(student.id)}的传递方法参数的EL 2.2特性,因为这个参数不会在图像URL中结束。 所以你真的需要把它们作为<f:param>来传递。


如果碰巧使用的是OmniFaces 2.0或更新的版本 ,那么考虑使用它的<o:graphicImage>而不是直接使用它,应用程序作用域getter方法直接委托给service方法并支持EL 2.2方法参数。

因此如此:

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

另见关于这个主题的博客 。

尝试包括一个MIMEtypes。 在你发布的例子中,你有它“”。 空白图像可能是因为它不会将stream识别为图像文件,因为您已将该字段设置为空string。 所以添加一个MIMEtypes的图像/ PNG或图像/ JPG,看看是否可行:

 String mimeType = "image/jpg"; StreamedContent file = new DefaultStreamedContent(bytes, mimeType, filename); 

这里有一些可能性(如果不是这样的话,请张贴全class)。

1)你没有正确初始化图像2)你的stream是空的,所以你什么都没有

我假设student.getImage()具有byte []的签名,所以首先确保该数据实际上是完整的,并表示一个图像。 其次 – 你不是指定一个内容types,应该是“图像/ JPG”或任何你使用的。

这里有一些样板代码来检查它,我使用Primefaces 2。

 /** 'test' package with 'test/test.png' on the path */ @RequestScoped @ManagedBean(name="imageBean") public class ImageBean { private DefaultStreamedContent content; public StreamedContent getContent() { if(content == null) { /* use your database call here */ BufferedInputStream in = new BufferedInputStream(ImageBean.class.getClassLoader().getResourceAsStream("test/test.png")); ByteArrayOutputStream out = new ByteArrayOutputStream(); int val = -1; /* this is a simple test method to double check values from the stream */ try { while((val = in.read()) != -1) out.write(val); } catch(IOException e) { e.printStackTrace(); } byte[] bytes = out.toByteArray(); System.out.println("Bytes -> " + bytes.length); content = new DefaultStreamedContent(new ByteArrayInputStream(bytes), "image/png", "test.png"); } return content; } } 

和一些标记…

 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:p="http://primefaces.prime.com.tr/ui" > <h:head> </h:head> <h:body> <p:graphicImage value="#{imageBean.content}" /> </h:body> </html> 

如果这个代码有效,那么你就可以正确设置。 尽pipe事实上它是stream的垃圾代码(不要在生产中使用它),但它应该给你一个解决的办法。 我的猜测是,你可能有一些事情发生在你的JPA或其他数据库框架,你是字节[]是空的或格式错误。 另外,你可能只是有一个内容types的问题。

最后,我将克隆bean中的数据,以便student.getImage()只会被复制到一个新的数组中,然后被使用。 这样,如果你有什么未知的东西(别的东西移动对象或改变字节[]你不是搞乱你的stream。

做类似的事情:

 byte[] data = new byte[student.getImage().length] for(int i = 0; i < data.length; i++) data[i] = student.getImage()[i]; 

所以你的bean有一个副本(或Arrays.copy() – 什么漂浮你的船)。 我不能强调这种简单的内容types通常是怎么回事。 祝你好运。

BalusC的答案是(和往常一样)是正确的。

但请记住一件事(正如他已经指出的那样)。 最后的请求是从浏览器完成的,从构造的<img>标签获取URL。 这不是在“jsf上下文”中完成的。

所以如果你尝试访问phaseId(logging或任何原因)

 context.getCurrentPhaseId().getName() 

这将导致一个NullPointerExceptionexception,并以某种方式误导性的错误消息,你会得到是:

 org.primefaces.application.resource.StreamedContentHandler () - Error in streaming dynamic resource. Error reading 'image' on type abSomeBean 

我花了相当长的时间才弄清楚是什么问题。