当前位置: 首页 > news >正文

实现在线预览pdf功能,后台下载PDF

  <!-- PDF预览模态框 --><n-modalv-model:show="pdfModalVisible"title="投诉统计报告预览":closable="false":mask-closable="false"@positive-click="closePdfModal"positive-text="关闭":width="900":height="1700":content-style="{ padding: 0, height: '170vh' }"><!-- PDF预览区域 --><iframeid="pdf-preview-iframe":src="pdfUrl"style="width: 100%; height: 900px; border: none;"></iframe></n-modal>

// PDF预览模态框状态
const pdfModalVisible = ref(false)
const pdfUrl = ref('')
const pdfFileName = ref('')// 预览并打印PDF
const handlePrint = async () => {try {// 准备参数let data = selectedMonth.value;data = new Date(new Date().getFullYear(), data).toISOString().slice(0, 7);// 显示加载提示window.$message.info('正在加载PDF文件...');// 调用 PDF 导出接口const apiUrl = `/api/manager/cmComplaintStatistics/exportReportPdf?yearMonth=${data}`;const response = await axios.get(apiUrl, {responseType: 'blob',headers: { 'x-token': `Bearer ${ssoClient.getToken()}` }});// 处理 PDF 流const blob = new Blob([response.data], { type: 'application/pdf' });pdfUrl.value = window.URL.createObjectURL(blob);pdfFileName.value = `${data}投诉统计报告.pdf`;// 显示PDF预览模态框pdfModalVisible.value = true;// 隐藏加载提示window.$message.success('PDF加载成功');} catch (error) {window.$message.error('获取PDF文件失败');console.error('接口请求错误:', error);}
};// 打印当前预览的PDF
const printCurrentPdf = () => {const pdfIframe = document.getElementById('pdf-preview-iframe');if (pdfIframe && pdfIframe.contentWindow) {pdfIframe.contentWindow.print();}
};// 下载当前预览的PDF
const downloadCurrentPdf = () => {const link = document.createElement('a');link.href = pdfUrl.value;link.download = pdfFileName.value;document.body.appendChild(link);link.click();document.body.removeChild(link);
};// 关闭PDF预览时释放资源
const closePdfModal = () => {pdfModalVisible.value = false;// 延迟释放URL以避免打印时资源已被回收setTimeout(() => {window.URL.revokeObjectURL(pdfUrl.value);pdfUrl.value = '';}, 3000);
};

后端:

@GetMapping("/exportReportPdf")@ApiOperationSupport(order = 8)@ApiOperation(value = "导出报告 PDF 文档", notes = "正式节点才能导出报告")public void exportReportPdf(@RequestParam String yearMonth, HttpServletResponse response) {try {// 设置 PDF 响应头response.setContentType("application/pdf");response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(yearMonth + "投诉统计报告.pdf", "UTF-8"));// 生成临时文件名String uuid = UUID.randomUUID().toString();String fileName = uuid + ".docx";Path docxPath = Paths.get(pathProperties.getReport(), uuid, fileName);Path pdfPath = Paths.get(pathProperties.getReport(), uuid, fileName + ".pdf");File docxFile = docxPath.toFile();File pdfFile = pdfPath.toFile();// 创建临时目录docxFile.getParentFile().mkdirs();// 生成 Word 文档XWPFTemplate template = getXwpfTemplate(null);try (FileOutputStream fos = new FileOutputStream(docxFile)) {template.write(fos);}// 转换 Word 到 PDFif (docxFile.exists()) {convertWordToPdf(docxFile, pdfFile);}// 将 PDF 文件内容写入响应流if (pdfFile.exists()) {try (InputStream in = new FileInputStream(pdfFile);OutputStream out = response.getOutputStream()) {byte[] buffer = new byte[4096];int bytesRead;while ((bytesRead = in.read(buffer)) != -1) {out.write(buffer, 0, bytesRead);}out.flush();} finally {// 可选:删除临时文件(建议在文件使用完毕后异步删除)docxFile.delete();pdfFile.delete();}} else {throw new IOException("PDF 文件生成失败");}} catch (Exception e) {log.error("导出PDF失败", e);try {response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "导出失败:" + e.getMessage());} catch (IOException ex) {log.error("设置响应错误信息失败", ex);}}}private XWPFTemplate getXwpfTemplate(CmComplaintVO cmComplaintVO) throws IOException {Map<String, Object> map = new HashMap<>();// 1. 处理文本参数(保持原有逻辑)map.put("work_order_time",Optional.ofNullable(LocalDateTime.now()).map(time -> time.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).orElse(null));// 处理图片的核心代码Resource imageResource = new ClassPathResource("templates/statistic_chart.png");try (InputStream imageStream = imageResource.getInputStream()) {// 1. 将输入流转换为 BufferedImage(直接从流转换,避免中间字节数组)BufferedImage bufferedImage = ImageIO.read(imageStream);if (bufferedImage == null) {throw new IOException("无法解析图片流,可能是图片格式不支持");}// 2. 使用 Pictures.ofBufferedImage() 创建图片对象PictureRenderData pictureData = Pictures.ofBufferedImage(bufferedImage, PictureType.PNG).size(712, 500) // 设置图片宽高(像素).create(); // 创建 PictureRenderDatamap.put("image", pictureData); // 绑定到模板占位符 {{image}}} catch (IOException e) {log.error("处理图片失败", e);// 可选:添加默认图片或抛出友好异常throw new RuntimeException("导出Word失败:图片处理异常", e);}// 3. 编译模板(必须绑定图片渲染策略)PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();Resource templateResource = resolver.getResource("classpath:/templates/cm_statistics.docx");Configure config = Configure.builder().bind("image", new PictureRenderPolicy()) // 绑定图片渲染策略.build();XWPFTemplate template = XWPFTemplate.compile(templateResource.getInputStream(), config).render(map);return template;}/*** 将Word文件转换为PDF文件* @param wordFile Word文件* @param pdfFile PDF文件*/public void convertWordToPdf(File wordFile, File pdfFile) {try (InputStream docxInputStream = new FileInputStream(wordFile);OutputStream outputStream = new FileOutputStream(pdfFile)) {IConverter converter = LocalConverter.builder().build();converter.convert(docxInputStream).as(DocumentType.DOCX).to(outputStream).as(DocumentType.PDF).execute();System.out.println("Word转PDF成功: " + wordFile.getName());} catch (Exception e) {e.printStackTrace();System.err.println("Word转PDF失败: " + wordFile.getName() + ", 错误: " + e.getMessage());} finally {// 删除临时文件if (wordFile.exists()) {wordFile.delete();}}}

http://www.xdnf.cn/news/1101637.html

相关文章:

  • 【web应用】若依框架中,使用Echarts导出报表为PDF文件
  • SSL与HTTP概述
  • 【网络编程】KCP——可靠的 UDP 传输协议——的知识汇总
  • 华为VS格行VS中兴VS波导随身WIFI6怎么选?流量卡OR随身WIFI,长期使用到底谁更香?
  • leetcode 3169. 无需开会的工作日 中等
  • day02-数组part02
  • 【LeetCode 热题 100】146. LRU 缓存——哈希表+双向链表
  • 十年架构心路:从单机到云原生的分布式系统演进史
  • OcsNG基于debian一键部署脚本
  • 老系统改造增加初始化,自动化数据源配置(tomcat+jsp+springmvc)
  • JVM--监控和故障处理工具
  • 正义的算法迷宫—人工智能重构司法体系的技术悖论与文明试炼
  • 区块链应用场景深度解读:从金融革命到社会治理的全方位革新
  • spark3 streaming 读kafka写es
  • 使用浏览器inspect调试wx小程序
  • 【HTML】俄罗斯方块(精美版)
  • AI产品经理面试宝典第7天:核心算法面试题-上
  • GPT3/chatGPT/T5/PaLM/LLaMA/GLM主流大语言模型的原理和差异
  • flutter redux状态管理
  • 文章发布易优CMS(Eyoucms)网站技巧
  • oracle
  • 【InnoDB存储引擎4】行结构
  • PDF转图片
  • 2025 年第十五届 APMCM 亚太地区大学生数学建模竞赛-B题 疾病的预测与大数据分析
  • 滑动窗口-3.无重复字符的最长子串-力扣(LeetCode)
  • 使用Python和AkShare轻松获取新闻联播文字稿:从数据获取到文本挖掘
  • vue3+ts div自由拖拽改变元素宽度
  • C++——构造函数的补充:初始化列表
  • UML 与 SysML 图表对比全解析:软件工程 vs 系统工程建模语言
  • ContextMenu的Item如何绑定命令