使用p:graphicImage和StreamedContent显示来自数据库或远程源的动态图像 [英] Display dynamic image from database or remote source with p:graphicImage and 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浏览器检索到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或更高版本,请考虑使用其 使用 另请参见博客. I'm trying to display image bytes which is saved in database as a
This returns a blank image. How is this caused and how can I solve it? The stdout prints the following:
The 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 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 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 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 In other words: never declare E.g. Where the Please note that this is a very special case wherein performing business logic in a getter method is completely legit, considering how the If you happen to use OmniFaces 2.0 or newer, then consider using its Thus so: With See also the blog on the subject. 这篇关于使用p:graphicImage和StreamedContent显示来自数据库或远程源的动态图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!<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();
}
}
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;
}
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>
requires a special getter method. It will namely be invoked twice per generated image, each in a completely different HTTP request.<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).<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. #{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.
<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.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.<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
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()));
}
}
}
<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>
.
<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();
}
}