使用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.
问题原因
- 流的复用冲突:在actionListener中提前打开
FileInputStream并直接传给StreamedContent的stream()方法,而Edge的PDF查看器会对资源发起多次HTTP请求(比如预加载、资源有效性验证)。第一次请求读取后流会被关闭,第二次请求时返回的是已关闭的流,触发IO异常。Chrome/Firefox的PDF处理逻辑仅发起一次请求,因此无问题。 - MIME类型错误:
contentType("pdf")不符合标准,PDF的正确MIME类型应为application/pdf,错误类型可能导致Edge无法正确解析资源,进而触发额外请求重试。 - 错误提示的误导:错误提示提到
@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
相关产品推荐
相关产品推荐

