大家好,我是前端小张同学,今天跟大家分享一下前端实现pdf下载和预览功能,应该如何去实现,话说这个问题还是我 女朋友 提出来的,那我就写一篇文章跟大家分享一下,希望本文能给大家带来帮助。
实现效果:
1:实现思路
- 前端使用pdfjs进行渲染
- 服务端读取pdf文件,转换为流文件
- 发送接口响应给前端进行渲染
2:前端如何实现
1: 我们前端有两种形式获取服务端流文件 , JSON or Blob
优劣势:json返回的数据字段更灵活,可根据不同情况来处理,Blob形式更简单,整个数据对象即为Blob类型。
2.1:使用Blob 实现pdf下载预览
首先Blob形式,我们需要定义接口的返回类型 responseType为 Blob,这种形式会将你的后端返回的所有数据整合成一个Bolb类型的数据,什么是Blob 这里我就不多讲了。
js
const createLinkDownload = (buffer, filename = '可回溯.pdf') => {
const blob = new Blob([buffer], { type: 'application/pdf' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
};
// blob 下载
const blobDownload = async () => {
const response = await axios.get('/api/download', {
responseType: 'blob', // 定义返回类型
});
createLinkDownload(response.data);
};
服务端 接口 逻辑
前端 response
可以在里看的出,即使我服务端返回了 加载成功等信息,但前端,依然没有这些字段,这是因为你在请求的时候已经将 responseType:Bold 了,而不是 application/json。
2.2:使用JSON格式实现pdf下载预览
使用JSON的核心就是将 后端读取到的pdf文件转换为base64编码,因为base64本身其实就是字符串, 它与Blob不一样,它需要将base64 转换为Buffer。
与Blob的不同点 json更明确字段返回,更具体。
js
const createLinkDownload = (buffer, filename = '可回溯.pdf') => {
const blob = new Blob([buffer], { type: 'application/pdf' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
};
// json 下载
const jsonDownload = async () => {
const response = await axios.post('/api/download', {});
console.log('response', response);
const arrerBuffer = base64ToBytes(response.data.data);
createLinkDownload(arrerBuffer);
};
3:后端如何实现
JS
const expressApp = require('express')
const path = require('path')
const fs = require('fs/promises')
const { commonResponse } = require('./common')
const app = expressApp()
const port = 8085
const resolveDir = (fileDir) => path.resolve(__dirname , fileDir)
const pdfDir = resolveDir('../assets/pdf/可回溯逻辑梳理.pdf')
app.get('/download' , (req , res) => {
// 使用 responseType
res.download(pdfDir , '可回溯pdf')
res.send(commonResponse('加载成功' , '1' , {}))
})
app.post('/download' , async (req , res) => {
// 使用JSON 形式
// 读取pdf内容 转换为 base64
const pdfStream = await fs.readFile(pdfDir , { encoding: 'base64' })
if(!pdfStream){
return res.send(commonResponse('加载失败' , '-2' , {}))
}
res.send(commonResponse('加载成功' , '1' , pdfStream))
})
app.listen(port , () =>{
console.log("服务启动成功" , port)
})
使用pdfjs转换为canvas渲染pdf
如何实现?
这里大家可以核心看理解一下 如何让pdf变清晰,核心要素就是缩放pdf,缩放为最适合屏幕的大小,即最清晰。
js
/**
* 加载页面
* @param pdfDocument pdf
* @param pageNumber 页数
*/
const renderPdfPage = async (pageNumber) => {
// 获取页面
const page = await pdfDocumentInstance.getPage(pageNumber);
console.log('page', page);
// 获取设备像素比
const dpr = window.devicePixelRatio || 1;
// 实际设备宽度
const deviceWidth = window.innerWidth || 414;
// 计算rem基准值(1rem = 100px)
const remBase = 100;
// 获取原始viewport以计算合适的缩放比例
const originalViewport = page.getViewport({ scale: 1.0 });
// 计算适合屏幕宽度的缩放比例
const fitScale = (deviceWidth * dpr) / originalViewport.width;
// 设置更高的基础缩放比例以提高清晰度
const qualityScale = 2.5;
// 设置画布尺寸,使用较高的缩放比例来提高清晰度
const viewport = page.getViewport({ scale: fitScale * qualityScale });
const canvasEls = pdfCanvas.value[pageNumber - 1];
const context = canvasEls.getContext('2d', { alpha: false });
// 设置画布的实际尺寸(考虑设备像素比和质量缩放)
canvasEls.height = viewport.height;
canvasEls.width = viewport.width;
// 设置画布的显示尺寸(使用rem单位)
canvasEls.style.height = `${viewport.height / (dpr * qualityScale) / remBase}rem`;
canvasEls.style.width = `${viewport.width / (dpr * qualityScale) / remBase}rem`;
// 启用图像平滑
context.imageSmoothingEnabled = true;
context.imageSmoothingQuality = 'high';
// 根据设备像素比和质量缩放来调整上下文
context.scale(1, 1);
// 渲染页面
const renderContext = {
canvasContext: context,
viewport: viewport,
};
await page.render(renderContext).promise;
if (maxRenderPdfNum.value > pageNumber) {
renderPdfPage(pageNumber + 1);
}
};
const getPdf = async () => {
console.log('1111');
const response = await getPdfFileStream();
console.log('response', response);
if (response && response.data?.STATUS === '1') {
pdfBufferData.value = response.data.data;
}
};
onMounted(async () => {
new pdfjsLib.GlobalWorkerOptions().workerSrc = pdfjsWorker || {};
// 发送请求获取pdf
await getPdf();
// 将 Blob 数据转换为 ArrayBuffer
const arrayBuffer = await base64ToBytes(pdfBufferData.value);
// 将 ArrayBuffer 数据加载到 pdfjs-dist
const loadingTask = pdfjsLib.getDocument({ data: arrayBuffer });
pdfDocumentInstance = await loadingTask.promise; // 加载pdf文档
maxRenderPdfNum.value = pdfDocumentInstance._pdfInfo.numPages; // 存储最大页数
// 渲染第一页
renderPdfPage(1);
});
加入我们的开发群,添加我微信(v:+86 13593659800),备注加群,进入我们内部交流群,了解更多前端知识。