You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

疑问

  1. 为何会出现PhaseId为null的情况?
  2. 如何处理RENDER_RESPONSE阶段的不同文件内容?
  3. 单视图中的图片为何无法显示?

解决方案与解释

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 09:54:51