blog
blog copied to clipboard
前端预览 PDF 的实现方式
结论 1.PDF 的预览行为是浏览器插件(浏览器厂商实现的)所实现的,浏览器对
Content-Type为application/pdf的 GET 请求的默认行为是下载文件 2.要实现可靠的,表现一致的网页端对 PDF 预览,只能使用 js 插件实现
后端提供PDF内容的方式
前端一般是通过调用后端接口去获取 PDF 文件(无论是静态 PDF 文件或是动态的 PDF 文件),不论请求类型,后端返回 PDF 内容通常有以下两种内容格式:
- 二进制: 此时响应的类型
Content-Type为application/pdf,浏览器对此类型的响应的默认处理方式为下载,在使用了预览插件的浏览器会不下载文件而是触发预览行为 - Base64: 此时响应的类型可能是
text/html,因为此时的 Base64 编码是放在返回体中的,Base64的处理方式实际是后台将 PDF 文件的二进制内容转换为 Base64 编码,此时的文件预览和上述二进制的处理方式基本是一样的,区别在于你需要告诉浏览器这串字符的格式是 Base64,需要将其转换为 PDF,在字符前面增加这串字符即可data:application/pdf;base64,
利用浏览器自带插件实现 PDF 预览
缺点
- 不是所有浏览器都有实现 PDF 文件预览,没有实现预览的浏览器会触发 PDF 文件的下载行为
- 浏览器实现预览的效果各不相同,而且和浏览器的版本有关(可能有些版本的预览功能有 bug)
优点
- 成本低,适合作为渐进增强
- 简单,不依赖于前端
如何开启或关闭浏览器的 PDF 预览功能
比如在 Chrome 浏览器,可以在下列菜单设置 PDF 预览功能的开关
设置 - 高级 - 内容设置 -PDF文档
实现示例
在新窗口打开 PDF 并进行预览或下载
<a href="/helloWorld.pdf" target="_blank">新窗口打开 PDF</a>
在页面内嵌 iframe 中打开 PDF 或进行下载
<iframe
src="/helloWorld.pdf"
width="100%"
height="500px"
>
This browser does not support PDFs. Please download the PDF to view it: <a href="/helloWorld.pdf">Download PDF</a>
</iframe>
在页面内嵌 iframe 中预览 Base64 编码格式的 PDF
<iframe
src="data:application/pdf;base64,other base64 code"
width="100%"
height="500px"
>
This browser does not support PDFs. Please download the PDF to view it: <a href="data:application/pdf;base64,other base64 code">Download PDF</a>
</iframe>
注意事项
- 除了
iframe标签外,还可以使用embedobject标签,实现示例和各自的优劣可见 前端预览PDF总结:iframe、embed、PDFObject、PDF.js - Base64 编码实现方式的性能更差
利用 PDF.js 插件实现 PDF 预览
PDF.js 是 mozilla 开源的 JS 库,大概实现原理是利用算法将 PDF 转换为 js 数据结构然后利用 canvas 在网页中复现
缺点
- 不支持低版本IE
- 体积约 3m (gzip前)
优点
- mozilla 出品,现在仍在更新
- 不依赖浏览器的PDF预览插件,大概实现原理是利用算法将 pdf 转换为数据结构,然后在 canvas 上输出
- 提供若干 API 接口,可以自行封装换页等插件
实现示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>editor</title>
<script src="/static/pdfjs/pdf.js"></script>
</head>
<body>
<canvas id="the-canvas"></canvas>
</body>
</html>
const pdfjsLib = window['pdfjs-dist/build/pdf']
// The workerSrc property shall be specified.
pdfjsLib.GlobalWorkerOptions.workerSrc = '/static/pdfjs/pdf.worker.js'
function initPDF () {
cosnt url = '/helloWorld.pdf' // pdf 文件的地址
const loadingTask = pdfjsLib.getDocument({
url: url,
}) // 创建加载文件对象
loadingTask.promise.then(function (pdf) {
// pdf 对象是文件加载完成后转换得来的,保存了文件内容以及相关的信息,如 pdf.numPages 能获取到 pdf 的页数
const num = 0 // 渲染第一页
pdf.getPage(num).then(function(page) {
console.log(`Page ${num} loaded`);
var scale = 1.5 // 放大倍数
var viewport = page.getViewport(scale);
const canvas = document.getElementById('the-canvas')
const ctx = canvas.getContext('2d')
// Prepare canvas using PDF page dimensions
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context
var renderContext = {
canvasContext: ctx,
viewport: viewport,
}
var renderTask = page.render(renderContext); // 生成 canvas 渲染任务
renderTask.then(function () {
console.log('Page rendered') // 渲染完毕
});
});
}).catch(function (reason) {
console.error('Error: ' + reason)
});
}
更多示例请见 mozilla/PDF.js 示例
参考资料
How to import Mozilla PDF.js in Vue project? vue + pdfjs-dist 实现pdf流文件在线预览 (解决了Error : Module parse failed: Unexpected token )
https://stackoverflow.com/questions/65750584/how-to-import-mozilla-pdf-js-in-vue-project