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

使用JSF+PrimeFaces展示PDF在Edge中无法流式传输资源的原因排查

JSF+PrimeFaces展示PDF在Edge中报错的问题分析

问题场景

使用Java JSF和PrimeFaces组件读取并展示PDF文件,功能在Firefox和Chrome中正常运行,但在Microsoft Edge中抛出IO异常,错误提示要求检查是否使用了@ViewScoped Bean,且已测试所有可能的作用域。

后端代码(读取文件并生成StreamedContent)

File file = new File(path);

try {
    fis = new FileInputStream(file);
} catch (FileNotFoundException e) {
    download = null;
}

if (fis != null) {   
    download = DefaultStreamedContent.builder()
                    .name("checklist.pdf")
                    .contentType("pdf")
                    .stream(() -> fis)
                    .build();

PrimeFaces instance = PrimeFaces.current();
instance.ajax().update("menuForm:urServiceRequestDialog");   
instance.executeScript("PF('widgetUrServiceRequestDialog').show()"); 

前端代码(PrimeFaces Media组件展示PDF)

<p:commandButton process="@form" widgetVar="widgetChecklistButton" actionListener="#{urServiceRequestControler.showChecklist(urEntryControler.selectedStopRegister)}"/>

<p:panel>  
    <div class="card">
       <p:media value="#{urServiceRequestControler.download}" cache="false" player="pdf">
           Brak pliku PDF.
       </p:media>
    </div>
</p:panel> 

Edge中抛出的错误

[2024-01-18T12:56:15.741+0100] [glassfish 4.1] [WARNING] []
[javax.enterprise.web] [tid: _ThreadID=26 _ThreadName=http-listener-1(1)] [timeMillis: 1705578975741] [levelValue: 900] [[
StandardWrapperValve[Faces Servlet]: Servlet.service() for servlet Faces
Servlet threw exception
java.io.IOException: Error in streaming dynamic resource. Verify you are not using a @ViewScoped bean.

问题原因

  1. 流的复用冲突:在actionListener中提前打开FileInputStream并直接传给StreamedContent的stream()方法,而Edge的PDF查看器会对资源发起多次HTTP请求(比如预加载、资源有效性验证)。第一次请求读取后流会被关闭,第二次请求时返回的是已关闭的流,触发IO异常。Chrome/Firefox的PDF处理逻辑仅发起一次请求,因此无问题。
  2. MIME类型错误:contentType("pdf")不符合标准,PDF的正确MIME类型应为application/pdf,错误类型可能导致Edge无法正确解析资源,进而触发额外请求重试。
  3. 错误提示的误导:错误提示提到@ViewScoped,但核心问题并非作用域本身,而是流的一次性特性与Edge多请求行为的冲突,更换作用域无法解决根本问题。

解决方法

修正后端代码:每次请求动态创建流

将StreamedContent的stream()方法改为每次调用时重新打开文件输入流,避免复用已关闭的流:

File file = new File(path);

download = DefaultStreamedContent.builder()
                .name("checklist.pdf")
                .contentType("application/pdf") // 修正为标准MIME类型
                .stream(() -> {
                    try {
                        return new FileInputStream(file);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                        return null;
                    }
                })
                .build();

PrimeFaces instance = PrimeFaces.current();
instance.ajax().update("menuForm:urServiceRequestDialog");   
instance.executeScript("PF('widgetUrServiceRequestDialog').show()"); 

这样每次Edge发起请求时,都会获取全新的文件输入流,解决流已关闭的问题。

额外注意事项

  • 确保path变量在每次调用stream()时能正确指向目标文件(若使用请求作用域Bean,需保证path状态可被正确保留)。
  • 若使用@ViewScoped,需确认视图状态未丢失,但核心修复仍为流的动态创建逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 07:20:20