PrimeFaces p:graphicImage与@RequestScoped Bean遇PhaseId null及图片不显示问题
问题场景与疑问
环境:JSF、JavaEE7、PrimeFaces 7.0、Wildfly 9
通过@RequestScoped的ImageView Bean从流加载图片,相关代码如下:
列表页面代码
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:p="http://primefaces.org/ui"> <h:outputStylesheet name="primeicons/primeicons.css" library="primefaces" /> <h:form> <p:scrollPanel> <ui:repeat value="${u.fileInfos}" var="image" id="manuelSwitcherImages"> <p:link href="${request.contextPath}/admin/single-fileInfo.xhtml"> <f:param name="fileName" value="${image.uniqueFileName}"/> <p:graphicImage value="${imageView.image}" width="500" id="image" rendered="${!image.uniqueFileName.isEmpty()}"> <f:param name="fileName" value="${image.uniqueFileName}" /> </p:graphicImage> </p:link> </ui:repeat> </p:scrollPanel> </h:form> </ui:composition>
ImageView Bean代码
@Named @RequestScoped public class ImageView implements Serializable { private static final long serialVersionUID = -1067751029307336424L; @Inject private AppProperties appProperties; private DefaultStreamedContent image; public ImageView() { } @PostConstruct public void setStream() { image = new DefaultStreamedContent(); FacesContext context = FacesContext.getCurrentInstance(); Map<String, String> map = context.getExternalContext().getRequestParameterMap(); String fileName = map.get("fileName"); System.out.println("PhaseId:" + context.getCurrentPhaseId() + " fileName:"+fileName); if (context.getCurrentPhaseId() != PhaseId.RENDER_RESPONSE) { if (fileName != null && !fileName.isEmpty()) { image.setContentType("image/jpeg"); FileInputStream fis; try { fis = new FileInputStream( new File(appProperties.getDocinfoRootPath() + File.separatorChar + fileName)); image.setStream(fis); } catch (FileNotFoundException e) { e.printStackTrace(); } } } } public DefaultStreamedContent getImage() { return image; } }
单图片视图代码
<?xml version="1.0" encoding="UTF-8"?> <ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:p="http://primefaces.org/ui" xmlns:dp="http://integrity.hu/dynap" template="/WEB-INF/templates/default.xhtml"> <ui:define name="content"> <h:form> <p:graphicImage value="${imageView.image}" id="singleimage" /> </h:form> </ui:define> </ui:composition>
运行日志
加载列表页时:
09:48:58,562 INFO [stdout] (default task-48) PhaseId:RENDER_RESPONSE 6 fileName:null 09:49:02,331 INFO [stdout] (default task-49) PhaseId:null fileName:IMG_20240801_103857_3362857899511577984.jpg
进入单图片视图时:
10:03:00,553 INFO [stdout] (default task-114) PhaseId:RENDER_RESPONSE 6 fileName:IMG_20240801_103857_3362857899511577984.jpg 10:03:00,764 INFO [stdout] (default task-115) PhaseId:null fileName:null
疑问
- 为何会出现PhaseId为null的情况?
- 如何处理RENDER_RESPONSE阶段的不同文件内容?
- 单视图中的图片为何无法显示?
解决方案与解释
1. PhaseId为null的原因
PrimeFaces的p:graphicImage在渲染页面时,只会生成指向Faces Servlet的图片URL;浏览器后续会单独发送GET请求加载图片,这个请求是纯资源请求,不会经过完整的JSF生命周期流程,因此FacesContext.getCurrentPhaseId()会返回null。
2. 处理RENDER_RESPONSE阶段的文件内容问题
当前代码的PhaseId判断逻辑完全错误:
- 页面渲染阶段(RENDER_RESPONSE):仅需生成图片URL,不需要加载实际图片流
- 图片资源请求阶段(PhaseId为null):这才是需要读取文件流返回图片的时机
修改逻辑:移除PhaseId判断,改为仅当fileName存在时加载图片流,页面渲染阶段直接返回空的DefaultStreamedContent即可。
3. 单视图图片无法显示的原因
单视图页面的p:graphicImage没有传递fileName参数,导致浏览器发送图片请求时无法获取到目标文件名,自然加载失败。
修改后的代码示例
修改后的ImageView Bean
将图片加载逻辑移到getImage()方法中,符合PrimeFaces资源加载规范:
@Named @RequestScoped public class ImageView implements Serializable { private static final long serialVersionUID = -1067751029307336424L; @Inject private AppProperties appProperties; public DefaultStreamedContent getImage() { FacesContext context = FacesContext.getCurrentInstance(); Map<String, String> requestParams = context.getExternalContext().getRequestParameterMap(); String fileName = requestParams.get("fileName"); // 页面渲染阶段,仅返回空流用于生成图片URL if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) { return DefaultStreamedContent.builder().build(); } // 图片资源请求阶段,加载实际图片 if (fileName != null && !fileName.isEmpty()) { try { File imageFile = new File(appProperties.getDocinfoRootPath() + File.separatorChar + fileName); InputStream inputStream = new FileInputStream(imageFile); return DefaultStreamedContent.builder() .contentType("image/jpeg") .stream(() -> inputStream) .build(); } catch (FileNotFoundException e) { e.printStackTrace(); // 加载失败时返回空流或默认图片 return DefaultStreamedContent.builder().build(); } } // fileName为空时返回空流 return DefaultStreamedContent.builder().build(); } }
修改后的单图片视图代码
添加f:param传递页面URL中的fileName参数:
<?xml version="1.0" encoding="UTF-8"?> <ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:p="http://primefaces.org/ui" xmlns:dp="http://integrity.hu/dynap" template="/WEB-INF/templates/default.xhtml"> <ui:define name="content"> <h:form> <p:graphicImage value="${imageView.image}" id="singleimage"> <f:param name="fileName" value="#{param.fileName}" /> </p:graphicImage> </h:form> </ui:define> </ui:composition>
内容的提问来源于stack exchange,提问作者Papp Zoltán
相关产品推荐
相关产品推荐

