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

PrimeFaces上传PDF后p:media无法预览,同标签另场景正常的问题求助

问题描述

我有一个文件上传组件,上传PDF后想用<p:media>标签预览,但无法显示文件。不过另一个通过html2pdf生成PDF并预览的组件,同样使用<p:media>却能正常运行。处理文件上传与预览的控制器为@SessionScoped,相关代码和依赖版本如下:

<h:panelGroup id="uploadPanel">
    <p:fileUpload 
        id="fileUploader" listener="#{Cntrl.handleFileUpload}"
        sizeLimit="30000" required="true"
        label="Attach Files" allowTypes="/(\.|\/)(pdf)$/i">
    </p:fileUpload>
</h:panelGroup>

<!-- Preview uploaded file-->
<h:panelGroup>
    <p:media 
        value="#{Cntrl.relativeFileName}" 
        width="100%" height="550px" 
        player="pdf" cache="false" >
        <h:outputText 
            value="Your browser can't display the file." 
            escape="false" />
    </p:media>
</h:panelGroup>

依赖版本:PrimeFaces 13.x.x、Java 17、Spring Boot 3.x.x

核心原因分析
  1. 资源访问方式不匹配:html2pdf生成的PDF通常是通过StreamedContent对象提供的,而上传后的文件直接使用相对路径时,<p:media>无法正确解析该路径对应的资源(比如文件存放在临时目录或非Web可访问路径)。
  2. 资源生命周期问题:@SessionScoped控制器中,如果上传的文件仅保存在内存或临时文件中,没有持续暴露为可访问资源,<p:media>请求资源时会找不到文件。
解决方案

方案1:用StreamedContent提供上传文件的内容

修改控制器,将上传的PDF转为StreamedContent对象,让<p:media>能直接读取文件流:

import org.primefaces.model.StreamedContent;
import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.UploadedFile;
import jakarta.faces.bean.SessionScoped;
import jakarta.inject.Named;
import java.io.IOException;
import java.io.Serializable;

@Named
@SessionScoped
public class Cntrl implements Serializable {
    private StreamedContent pdfPreview;

    public void handleFileUpload(FileUploadEvent event) {
        UploadedFile uploadedFile = event.getFile();
        // 构建预览用的StreamedContent
        pdfPreview = DefaultStreamedContent.builder()
                .name(uploadedFile.getFileName())
                .contentType("application/pdf")
                .stream(() -> {
                    try {
                        return uploadedFile.getInputStream();
                    } catch (IOException e) {
                        throw new RuntimeException("Failed to read uploaded file", e);
                    }
                })
                .build();
    }

    public StreamedContent getPdfPreview() {
        return pdfPreview;
    }
}

更新页面的<p:media>标签:

<!-- Preview uploaded file-->
<h:panelGroup id="previewPanel">
    <p:media 
        value="#{Cntrl.pdfPreview}" 
        width="100%" height="550px" 
        player="pdf" cache="false" >
        <h:outputText 
            value="Your browser can't display the file." 
            escape="false" />
    </p:media>
</h:panelGroup>

方案2:将上传文件存储到Web可访问目录

如果要继续使用相对路径,需将上传文件保存到Spring Boot的静态资源目录(如src/main/resources/static/uploads),确保文件能通过HTTP访问:

import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.UploadedFile;
import jakarta.faces.bean.SessionScoped;
import jakarta.inject.Named;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;

@Named
@SessionScoped
public class Cntrl implements Serializable {
    private String relativeFileName;
    // 静态资源目录,Spring Boot会自动映射该目录下的资源到/uploads路径
    private final String UPLOAD_DIR = System.getProperty("user.dir") + "/src/main/resources/static/uploads/";

    public void handleFileUpload(FileUploadEvent event) {
        UploadedFile uploadedFile = event.getFile();
        File uploadDir = new File(UPLOAD_DIR);
        if (!uploadDir.exists()) {
            uploadDir.mkdirs();
        }
        File destFile = new File(UPLOAD_DIR + uploadedFile.getFileName());
        try (FileOutputStream fos = new FileOutputStream(destFile)) {
            fos.write(uploadedFile.getContents());
            // 构建相对URL,供<p:media>访问
            relativeFileName = "/uploads/" + uploadedFile.getFileName();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String getRelativeFileName() {
        return relativeFileName;
    }
}

注意:生产环境建议使用外部存储路径(如配置在application.properties中的自定义目录),避免项目目录变更导致资源不可访问。

方案3:强制刷新预览区域

上传完成后,需要更新预览组件以获取最新资源,给<p:fileUpload>添加update属性指定预览面板:

<p:fileUpload 
    id="fileUploader" listener="#{Cntrl.handleFileUpload}"
    sizeLimit="30000" required="true"
    label="Attach Files" allowTypes="/(\.|\/)(pdf)$/i"
    update="previewPanel"> <!-- 更新预览面板 -->
</p:fileUpload>

<!-- Preview uploaded file-->
<h:panelGroup id="previewPanel"> <!-- 添加id供update引用 -->
    <p:media 
        value="#{Cntrl.relativeFileName}" 
        width="100%" height="550px" 
        player="pdf" cache="false" >
        <h:outputText 
            value="Your browser can't display the file." 
            escape="false" />
    </p:media>
</h:panelGroup>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 12:57:35