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

PrimeFaces中StreamedContent与p:media无法更新PDF内容求助

解决PrimeFaces中动态PDF重复显示首次文件的问题

嘿,我完全懂你遇到的这个糟心问题——这其实是PrimeFaces里StreamedContent结合无状态Bean时常见的状态管理+浏览器缓存双重坑,我来给你一步步拆解解决办法:

问题根源分析

  1. 无状态Bean的状态丢失:你用了@Stateless EJB,这类Bean是池化复用的,每次请求可能拿到不同的实例,你在verDocumento里设置的streamedContent和doc变量,下一次请求时可能已经被其他请求覆盖或者丢失了。
  2. 浏览器缓存:p:media加载PDF时,浏览器会默认缓存资源URL,第一次加载后,后续请求会直接用缓存,不会再去服务器拿新的流。
  3. StreamedContent的加载时机:p:media的value是在对话框显示后才发起单独的资源请求,这时候你之前设置的实例变量已经不在当前Bean实例里了。

具体解决方案

1. 把Bean改成视图作用域(ViewScoped)

无状态Bean不适合这种需要在同一页面多次交互保留状态的场景,换成JSF的@ViewScoped,让Bean和当前视图绑定,状态会一直保留到你离开这个页面:

@Named(value = "visualizarDocumento")
@ViewScoped // 替换原来的@Stateless
public class VisualizarDocumentoController implements Serializable { // 必须实现Serializable
    private InformacionDocumento currentDoc; // 保存当前选中的文档,替代原来的doc变量
    // 不需要再把streamedContent作为实例变量保存

    public void verDocumento(InformacionDocumento documento) {
        this.currentDoc = documento; // 只需要保存当前选中的文档
        RequestContext requestContext = RequestContext.getCurrentInstance();
        requestContext.update(":visualizacion");
        requestContext.execute("PF('visualizarPDF').show()");
    }

    public StreamedContent getStreamedContent() {
        // 如果没有选中文档,返回空的默认流
        if (currentDoc == null) {
            return DefaultStreamedContent.builder()
                    .contentType("application/pdf")
                    .build();
        }
        // 每次请求都重新解码生成流
        byte[] decode = Base64.getDecoder().decode(currentDoc.getArchivo().getBase64());
        ByteArrayInputStream bis = new ByteArrayInputStream(decode);
        return DefaultStreamedContent.builder()
                .name(currentDoc.getNombreArchivo()) // 可选:设置文件名,方便用户下载时识别
                .contentType("application/pdf")
                .stream(() -> bis)
                .build();
    }

    // currentDoc需要getter,因为在XHTML里要用到
    public InformacionDocumento getCurrentDoc() {
        return currentDoc;
    }

    public void setCurrentDoc(InformacionDocumento currentDoc) {
        this.currentDoc = currentDoc;
    }
}

2. 修改XHTML的p:media,添加防缓存参数

给p:media加上动态参数,让浏览器认为每次都是新的请求,彻底绕过缓存:

<p:dialog widgetVar="visualizarPDF" modal="true">
    <h:form id="visualizacion" >
        <p:media value="#{visualizarDocumento.streamedContent}" 
                 width="750" 
                 height="560" 
                 player="pdf">
            <!-- 用当前文档的ID和时间戳作为参数,确保每次请求URL不同 -->
            <f:param name="docId" value="#{visualizarDocumento.currentDoc.id}" />
            <f:param name="t" value="#{now.time}" />
        </p:media>
    </h:form>
</p:dialog>

3. 优化XHTML的表单结构(可选但推荐)

把h:form从p:column里移出来,放在p:dataTable外面,避免每个按钮都单独生成表单(PrimeFaces允许在dataTable外面用一个表单包裹所有按钮):

<h:form>
    <p:dataTable var="documento" value="#{docController.obtenerDocumentos()}" filteredValue="#{docController.filteredValue}" >
        <!-- 其他列 -->
        <p:column>
            <f:facet name="header">
                <h:outputText value="Acciones"/>
            </f:facet>
            <center>
                <p:commandButton styleClass="ui-priority-primary" 
                                 icon="fa fas fa-eye" 
                                 update=":visualizacion" 
                                 action="#{visualizarDocumento.verDocumento(documento)}" 
                                 value="Ver documento" 
                                 onstart="PF('cargando').show()" 
                                 onsuccess="PF('cargando').hide()"/>
                <br/>
                <p:commandButton styleClass="ui-priority-primary" 
                                 icon="fa fas fa-align-justify" 
                                 action="#{docController.respuestaDocumento(documento)}" 
                                 value="Ver respuestas"/>
            </center>
        </p:column>
    </p:dataTable>
</h:form>

为什么这样能解决问题?

  • @ViewScoped:保证了Bean的状态和当前页面绑定,每次点击不同文档时,currentDoc都会被正确更新,不会因为无状态Bean的池化导致状态混乱。
  • 动态生成StreamedContent:每次p:media请求资源时,都会基于当前的currentDoc重新解码生成流,确保拿到的是最新选中的文档。
  • 防缓存参数:docId和时间戳让浏览器每次请求的URL都不一样,彻底避免了缓存旧PDF的问题。

内容的提问来源于stack exchange,提问作者Julian Solarte

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:30:43