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
核心原因分析
- 资源访问方式不匹配:html2pdf生成的PDF通常是通过
StreamedContent对象提供的,而上传后的文件直接使用相对路径时,<p:media>无法正确解析该路径对应的资源(比如文件存放在临时目录或非Web可访问路径)。 - 资源生命周期问题:
@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
相关产品推荐
相关产品推荐

