前端Vue+后端Node.js实现PDF下载和预览功能

前端 潘老师 1个月前 (03-20) 37 ℃ (0) 扫码查看

今天来给大伙分享一下前端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下载和预览功能的全部内容啦!在实际开发过程中,大家可以根据具体的业务需求对代码进行调整和优化。要是在实现过程中遇到什么问题,欢迎在评论区留言交流!


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/front/16053.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】