章
目
录
今天来给大伙分享一下前端Vue结合后端Node.js实现PDF下载和预览功能的具体方法。先给大伙看看最终实现的效果:有一个记录了不同版本更新信息的表格,里面包含版本号、更新内容、负责人和更新时间等信息。在实际应用场景里,这个表格数据可能就存放在PDF文件中,咱们要做的就是实现对这个PDF的下载和预览。
一、实现思路
整体实现思路是这样的:前端借助pdfjs来渲染PDF文件;后端负责读取PDF文件,并把它转换为流文件,之后通过接口响应发送给前端,前端收到数据后进行渲染展示。
二、前端实现方式
前端从服务端获取流文件有JSON和Blob两种形式,这两种方式各有优劣。JSON返回的数据字段更灵活,开发者能根据不同的业务情况进行针对性处理;Blob形式则相对简单直接,后端返回的数据会被整合成一个Blob类型的数据对象。
2.1 使用Blob实现PDF下载预览
采用Blob形式时,需要把接口的返回类型responseType
设置为Blob
。这种方式会将后端返回的所有数据打包成一个Blob类型的数据。这里简单解释一下Blob,它是一种二进制大对象,在处理文件数据时经常会用到。下面看看具体代码:
// 创建下载链接的函数,接收文件数据buffer和文件名filename(默认为'可回溯.pdf')
const createLinkDownload = (buffer, filename = '可回溯.pdf') => {
// 根据文件数据创建Blob对象,指定类型为'application/pdf'
const blob = new Blob([buffer], { type: 'application/pdf' });
// 创建一个a标签用于触发下载
const link = document.createElement('a');
// 为a标签设置下载链接,通过URL.createObjectURL将Blob对象转换为可访问的URL
link.href = URL.createObjectURL(blob);
// 设置下载的文件名
link.download = filename;
// 模拟点击a标签,触发下载操作
link.click();
};
// 执行Blob下载的函数
const blobDownload = async () => {
// 发送axios的get请求到'/api/download'接口,设置responseType为'blob'
const response = await axios.get('/api/download', {
responseType: 'blob',
});
// 调用createLinkDownload函数,传入响应数据进行下载
createLinkDownload(response.data);
};
再看看服务端的接口逻辑,这里用的是Node.js和Express框架:
从上面的代码可以看出,当使用Blob形式时,前端请求接口后,即便服务端返回了加载成功等信息,前端也获取不到这些字段,因为请求时设置了responseType: 'blob'
,而不是application/json
。
2.2 使用JSON格式实现PDF下载预览
使用JSON格式的关键在于,后端要把读取到的PDF文件转换为base64编码。由于base64本质是字符串,和Blob不同,前端获取到数据后,需要将base64转换为Buffer才能进一步处理。JSON格式的优势在于能更明确地返回字段,数据结构更清晰。下面是具体实现代码:
// 创建下载链接的函数,和Blob方式中的这个函数作用相同
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 () => {
// 发送axios的post请求到'/api/download'接口
const response = await axios.post('/api/download', {});
// 打印响应数据,方便调试查看
console.log('response', response);
// 将base64编码的数据转换为ArrayBuffer
const arrerBuffer = base64ToBytes(response.data.data);
// 调用createLinkDownload函数,传入转换后的ArrayBuffer进行下载
createLinkDownload(arrerBuffer);
};
三、后端实现代码
后端代码和前面Blob方式中处理下载接口的代码基本一致,主要是通过Express框架设置接口,读取PDF文件并进行相应处理。代码如下:
// 引入express框架
const expressApp = require('express')
// 用于处理文件路径
const path = require('path')
// 用于异步操作文件系统
const fs = require('fs/promises')
// 自定义的通用响应处理函数
const { commonResponse } = require('./common')
// 创建express应用实例
const app = expressApp()
// 设置服务端口
const port = 8085
// 定义一个函数,用于解析相对路径为绝对路径
const resolveDir = (fileDir) => path.resolve(__dirname, fileDir)
// 指定要下载的PDF文件路径
const pdfDir = resolveDir('../assets/pdf/可回溯逻辑梳理.pdf')
// 处理GET请求的下载接口
app.get('/download', (req, res) => {
// 使用res.download方法直接下载指定路径的PDF文件,并命名为'可回溯pdf'
res.download(pdfDir, '可回溯pdf')
// 发送通用响应,告知前端加载成功
res.send(commonResponse('加载成功', '1', {}))
})
// 处理POST请求的下载接口
app.post('/download', async (req, res) => {
// 以base64编码方式读取PDF文件内容
const pdfStream = await fs.readFile(pdfDir, { encoding: 'base64' })
// 如果读取失败,返回加载失败的响应
if (!pdfStream) {
return res.send(commonResponse('加载失败', '-2', {}))
}
// 读取成功则返回加载成功的响应,包含读取的PDF文件内容
res.send(commonResponse('加载成功', '1', pdfStream))
})
// 启动服务,监听指定端口
app.listen(port, () => {
console.log("服务启动成功", port)
})
四、使用pdfjs转换为canvas渲染PDF
在实现PDF预览时,使用pdfjs将PDF转换为canvas进行渲染。这里的核心要点是如何让PDF显示得更清晰,关键就在于对PDF进行缩放,使其适配屏幕大小。下面这段代码详细展示了实现过程:
// 渲染PDF页面的函数,接收要渲染的页码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 });
// 根据设备宽度和原始viewport宽度计算适合屏幕的缩放比例
const fitScale = (deviceWidth * dpr) / originalViewport.width;
// 设置一个更高的基础缩放比例,用于提高清晰度
const qualityScale = 2.5;
// 根据计算的缩放比例获取新的viewport,用于设置画布尺寸
const viewport = page.getViewport({ scale: fitScale * qualityScale });
// 获取用于渲染PDF的canvas元素
const canvasEls = pdfCanvas.value[pageNumber - 1];
// 获取canvas的2d绘图上下文
const context = canvasEls.getContext('2d', { alpha: false });
// 设置canvas的实际高度和宽度,考虑缩放比例
canvasEls.height = viewport.height;
canvasEls.width = viewport.width;
// 设置canvas的显示高度和宽度,使用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);
// 定义渲染上下文对象,包含canvas绘图上下文和viewport信息
const renderContext = {
canvasContext: context,
viewport: viewport,
};
// 渲染页面,并等待渲染完成
await page.render(renderContext).promise;
// 如果还有更多页面需要渲染,递归调用渲染下一页
if (maxRenderPdfNum.value > pageNumber) {
renderPdfPage(pageNumber + 1);
}
};
// 获取PDF文件数据的函数
const getPdf = async () => {
// 打印日志,方便调试查看
console.log('1111');
// 发送请求获取PDF文件流
const response = await getPdfFileStream();
// 打印响应数据,方便调试查看
console.log('response', response);
// 如果响应存在且数据中的STATUS字段为'1',说明获取数据成功
if (response && response.data?.STATUS === '1') {
// 将获取到的PDF数据存储到响应式变量pdfBufferData中
pdfBufferData.value = response.data.data;
}
};
// 组件挂载完成后的生命周期钩子函数
onMounted(async () => {
// 设置pdfjs的worker源,确保其能正常工作
new pdfjsLib.GlobalWorkerOptions().workerSrc = pdfjsWorker || {};
// 发送请求获取PDF文件数据
await getPdf();
// 将base64编码的PDF数据转换为ArrayBuffer
const arrayBuffer = await base64ToBytes(pdfBufferData.value);
// 使用pdfjsLib加载PDF文件数据
const loadingTask = pdfjsLib.getDocument({ data: arrayBuffer });
// 等待PDF文档加载完成
pdfDocumentInstance = await loadingTask.promise;
// 获取PDF文档的总页数并存储到响应式变量maxRenderPdfNum中
maxRenderPdfNum.value = pdfDocumentInstance._pdfInfo.numPages;
// 开始渲染第一页
renderPdfPage(1);
});
以上就是前端Vue结合后端Node.js实现PDF下载和预览功能的全部内容啦!在实际开发过程中,大家可以根据具体的业务需求对代码进行调整和优化。要是在实现过程中遇到什么问题,欢迎在评论区留言交流!