前端 PDF 渲染与下载实现

文章目录

    • [一、浏览器中 PDF 的几种实现方式](#一、浏览器中 PDF 的几种实现方式)
    • [二、pdf.js 简介](#二、pdf.js 简介)
    • [三、PDF 渲染原理](#三、PDF 渲染原理)
    • [四、Vue 中实现 PDF 渲染](#四、Vue 中实现 PDF 渲染)
    • [五、实现 PDF 渲染逻辑](#五、实现 PDF 渲染逻辑)
    • [六、PDF 下载功能实现](#六、PDF 下载功能实现)
    • [七、PDF 下载实现方式对比](#七、PDF 下载实现方式对比)
    • [八、PDF 渲染优化建议](#八、PDF 渲染优化建议)
      • [1 使用高清渲染](#1 使用高清渲染)
      • [2 避免重复渲染](#2 避免重复渲染)
      • [3 大文件建议分页加载](#3 大文件建议分页加载)
    • 九、常见问题
      • [PDF 渲染模糊](#PDF 渲染模糊)
      • [worker 加载失败](#worker 加载失败)
      • [大 PDF 渲染卡顿](#大 PDF 渲染卡顿)
    • 十、总结
      • [PDF 预览](#PDF 预览)
      • [PDF 下载](#PDF 下载)

在企业项目中,经常会遇到 PDF 文件预览和下载 的需求,例如:

  • 电子处方预览
  • 合同文件查看
  • 报告文件下载
  • 发票或账单展示

浏览器虽然可以直接打开 PDF,但在实际项目中通常需要 嵌入到页面中进行自定义展示,例如:

  • 自定义弹窗查看
  • 控制分页渲染
  • 自定义下载按钮
  • 控制缩放和样式

因此,很多项目都会使用 pdf.js 来实现前端 PDF 渲染。

本文将介绍 Vue 项目中基于 pdf.js 实现 PDF 预览和下载的完整方案

一、浏览器中 PDF 的几种实现方式

在前端开发中,常见的 PDF 渲染方式主要有三种:

方式 优点 缺点
iframe 实现简单 UI不可控
embed 浏览器原生支持 样式难控制
pdf.js 可完全自定义 需要手动渲染

例如 iframe 方式:

html 复制代码
<iframe src="file.pdf"></iframe>

这种方式虽然简单,但存在很多问题:

  • 样式无法控制
  • 无法自定义工具栏
  • 不方便嵌入业务页面

因此在企业项目中,更推荐使用:

text 复制代码
pdf.js

二、pdf.js 简介

pdf.js 是 Mozilla 开源的 PDF 解析库,可以在浏览器中解析并渲染 PDF 文件。

GitHub 地址:

text 复制代码
https://github.com/mozilla/pdf.js

主要能力:

  • 解析 PDF 文件
  • 将 PDF 渲染为 Canvas
  • 支持分页渲染
  • 支持缩放
  • 支持文本选择

安装方式:

bash 复制代码
npm install pdfjs-dist

三、PDF 渲染原理

pdf.js 的渲染流程大致如下:

text 复制代码
加载 PDF 文件
↓
解析 PDF 文档
↓
获取 PDF 页数
↓
逐页解析
↓
生成 viewport
↓
Canvas 渲染
↓
显示到页面

简单来说就是:

text 复制代码
PDF 文件 → pdf.js 解析 → Canvas 绘制 → 页面展示

四、Vue 中实现 PDF 渲染

在 Vue 中,我们可以通过 pdf.js 将 PDF 渲染到 canvas 上。

模板代码:

html 复制代码
<template>
  <dialog :title="dialogTitle" v-model="dialogVisible" width="880px">

    <div ref="pdfRef" class="pdf-view"></div>

    <template #footer>
      <el-button
        @click="downLoad"
        type="primary"
        :disabled="formLoading"
      >
        下 载
      </el-button>

      <el-button @click="dialogVisible = false">
        关 闭
      </el-button>
    </template>

  </dialog>
</template>

其中:

text 复制代码
pdfRef

用于存放渲染后的 PDF 页面。

五、实现 PDF 渲染逻辑

核心代码如下:

javascript 复制代码
import { nextTick } from "vue"
import PdfjsWorker from "pdfjs-dist/build/pdf.worker.js?worker"

async function initPdf() {

  const PDFJS = await import("pdfjs-dist/build/pdf.js")

  if (typeof window !== "undefined" && "Worker" in window) {
    PDFJS.GlobalWorkerOptions.workerPort = new PdfjsWorker()
  }

  const loadingTask = PDFJS.getDocument({ url: pdfSrc.value })

  const pdf = await loadingTask.promise

  pdfRef.value.innerHTML = ""

  for (let i = 1; i <= pdf.numPages; i++) {

    const page = await pdf.getPage(i)

    const pixelRatio = window.devicePixelRatio || 2

    const viewport = page.getViewport({ scale: 1 })

    const divPage = document.createElement("div")

    divPage.className = "page"

    const canvas = document.createElement("canvas")

    divPage.appendChild(canvas)

    pdfRef.value.appendChild(divPage)

    canvas.width = viewport.width * pixelRatio

    canvas.height = viewport.height * pixelRatio

    const renderContext = {
      canvasContext: canvas.getContext("2d"),
      viewport: viewport,
      transform: [pixelRatio, 0, 0, pixelRatio, 0, 0]
    }

    await page.render(renderContext).promise

    divPage.className = "page complete"

  }

}

代码主要流程:

text 复制代码
加载 PDF
↓
获取页数
↓
循环获取每一页
↓
创建 Canvas
↓
Canvas 渲染
↓
插入页面

最终效果就是:

text 复制代码
每一页 PDF → 一个 Canvas

六、PDF 下载功能实现

除了预览外,通常还需要提供 PDF 下载功能

实现思路:

text 复制代码
fetch 请求 PDF
↓
转换为 Blob
↓
创建下载链接
↓
触发浏览器下载

示例代码:

javascript 复制代码
const downLoad = async () => {

  dialogVisible.value = false

  const link = document.createElement("a")

  link.style.display = "none"

  try {

    const response = await fetch(pdfSrc.value)

    if (!response.ok) {
      throw new Error(`Network response was not ok`)
    }

    const blob = await response.blob()

    link.href = URL.createObjectURL(blob)

    link.download = `${patientName.value}.pdf`

    document.body.appendChild(link)

    link.click()

    document.body.removeChild(link)

    URL.revokeObjectURL(link.href)

  } catch (error) {

    console.error("PDF 下载失败:", error)

  }

}

关键步骤:

text 复制代码
fetch
↓
blob
↓
createObjectURL
↓
a标签下载

这种方式相比直接链接下载更灵活。

七、PDF 下载实现方式对比

常见的 PDF 下载方式主要有两种:

方式一:直接链接下载

javascript 复制代码
const link = document.createElement("a")
link.href = pdfUrl
link.download = "file.pdf"
link.click()

优点:

  • 实现简单

缺点:

  • 无法携带 token
  • 无法处理错误

方式二:fetch 下载(推荐)

javascript 复制代码
fetch → blob → createObjectURL → a标签下载

优点:

  • 支持 token
  • 可自定义文件名
  • 可处理异常

因此在企业项目中更推荐使用。

八、PDF 渲染优化建议

在实际项目中,如果 PDF 文件较大,需要注意性能问题。

1 使用高清渲染

推荐使用:

javascript 复制代码
window.devicePixelRatio

例如:

javascript 复制代码
const pixelRatio = window.devicePixelRatio || 2

这样可以避免 PDF 模糊。

2 避免重复渲染

每次渲染前需要清空容器:

javascript 复制代码
pdfRef.value.innerHTML = ""

否则会出现:

text 复制代码
重复页面

3 大文件建议分页加载

如果 PDF 有很多页,例如:

text 复制代码
100页
200页

一次性渲染会导致:

  • 页面卡顿
  • 首屏加载慢

优化方式:

text 复制代码
滚动懒加载

只渲染当前可见页面。

九、常见问题

PDF 渲染模糊

原因:

text 复制代码
canvas 分辨率不足

解决:

javascript 复制代码
devicePixelRatio

worker 加载失败

错误示例:

text 复制代码
Setting up fake worker failed

原因:

text 复制代码
worker 路径配置错误

解决方式:

javascript 复制代码
PDFJS.GlobalWorkerOptions.workerPort = new PdfjsWorker()

大 PDF 渲染卡顿

原因:

text 复制代码
一次性渲染所有页面

解决:

text 复制代码
分页渲染
或
滚动懒加载

十、总结

在前端项目中,PDF 功能通常包含两个部分:

PDF 预览

实现方式:

text 复制代码
pdf.js
↓
解析 PDF
↓
Canvas 渲染

PDF 下载

实现方式:

text 复制代码
fetch
↓
blob
↓
createObjectURL
↓
a 标签下载

推荐实践:

功能 推荐方案
PDF 预览 pdf.js
PDF 下载 fetch + blob
大文件优化 懒加载

通过 pdf.js,我们可以在前端 完全控制 PDF 的展示方式,从而更好地适配业务需求。


👉点击进入 我的网站

相关推荐
jiayong232 小时前
可视化流程设计器技术对比:钉钉风格 vs BPMN
java·前端·钉钉
前端不太难2 小时前
Flutter Web / Desktop 为什么“能跑但不好用”?
前端·flutter·状态模式
甘露s2 小时前
新手入门:传统 Web 开发与前后端分离开发的区别
开发语言·前端·后端·web
双河子思2 小时前
自动化控制逻辑建模方法
前端·数据库·自动化
wsad05322 小时前
Vue.js 整合传统 HTML 项目:注册页面实战教程
前端·vue.js·html
XXYBMOOO2 小时前
Flarum 主题定制:从零打造你的赛博朋克/JOJO 风格社区(含全套 CSS 源码)
前端·css
升鲜宝供应链及收银系统源代码服务2 小时前
升鲜宝生鲜配送供应链管理系统生产加工子模块的详细表设计说明
java·大数据·前端·数据库·bootstrap·供应链系统·生鲜配送
行者-全栈开发2 小时前
43 篇系统实战:uni-app 从入门到架构师成长之路
前端·typescript·uni-app·vue3·最佳实践·企业级架构
泉城老铁2 小时前
一分钟搞定SpringBoot+Vue3 整合 SSE 实现实时消息推送
前端·vue.js·后端