美文网首页
前端生成pdf解决方案

前端生成pdf解决方案

作者: Mr无愧于心 | 来源:发表于2022-02-21 15:26 被阅读0次

    方案一:html2canvas + jspdf

    缺点:不能获取页面上的文字,超链接等(用途有限)

    savePdf=(element, title) => {  //html2canvas+jspdf实现方案
           html2canvas(element, {
             useCORS: true, // 【重要】开启跨域配置
             allowTaint: true, // 允许跨域图片
             taintTest: false, // 是否在渲染前测试图片
             scale: 2,
           }).then((canvas) => {
             const contentWidth = canvas.width;
             const contentHeight = canvas.height;
    
             // 一页pdf显示html页面生成的canvas高度;
             const pageHeight = contentWidth / 596 * 842;
             // 未生成pdf的html页面高度
             let leftHeight = contentHeight;
             // 页面偏移
             let position = 0;
    
             // 每页的尺寸[595,842],html页面生成的canvas在pdf中图片的宽高
             const imgWidth = 596;
             const imgHeight = 596 / contentWidth * contentHeight;
    
             const pageData = canvas.toDataURL('image/jpeg', 1.0);
    
             const pdf = new JsPDF('', 'pt', 'a4');
    
             // 有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(842)
             // 当内容未超过pdf一页显示的范围,无需分页
             if (leftHeight < pageHeight) {
               pdf.addImage(pageData, 'JPEG', 20, 0, imgWidth, imgHeight);
             } else {
               while (leftHeight > 0) {
                 pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
                 leftHeight -= (pageHeight + 16);// 16是页面上每页的margin
                 position -= 842 + 16;// 16是页面上每页的margin
                 // 避免添加空白页
                 console.log(leftHeight)
                 if (leftHeight > 300) {
                   pdf.addPage();
                 }
               }
             }
             console.log(pdf)
             pdf.save(`${title}.pdf`);
           });
         }
    

    方案二: 使用puppeteer + node端实现

    能够获取页面上的文字,超链接等。但是出现问题难以调试
    官方文档:https://zhaoqize.github.io/puppeteer-api-zh_CN/

    const puppeteer = require('puppeteer');
    const fs = require('fs');
    const send = require('koa-send');
    const path = require('path');
    const moment = require('moment')
    
    // 生成pdf
    async function generatePdf(pdfname, date, cookies) {
      const browser = await puppeteer.launch({
        args: ['--disable-dev-shm-usage', '--no-sandbox'],
        ignoreHTTPSErrors: true,
        headless: true,
      });
      const page = await browser.newPage();
      const waitUntil = 'networkidle0'; // 代表页面的接口都返回后再渲染
      await page.setCookie(...cookies);
    
      await page.goto(url, {// 导航到的地址
        waitUntil, // 不再有网络连接时触发
        timeout: 30 * 60 * 60 * 1000,
      });
      await page.addStyleTag({// 跟需要转pdf的页面修改样式,可以隐藏一些不想展示的内容
        content: `
            html {
                -webkit-filter: opacity(1) !important;
            }
           `,
      });
    
    
      await page.pdf({
        path: `${pdfname + date}.pdf`, // 名称
        printBackground: true, // 展示背景图
        width: 596, // 每页画的宽度
        height: 843, // 每页画的高度
      });
      await browser.close();
      return pdfname;
    }
    
    
    class ApiController {
      async getPdf(ctx) {
        const dateTime = moment().format('YYYYMMDD')
        const { start_day } = ctx.query;
    //可以传一些需要渲染页面所需的参数,类似登录的token、页面需要的参数等(无法通过其他方式传值,需要转pdf的页面直接从cookie里边读取想要的信息即可)
        const cookies = [
          {
            name: 'token',
            value: ctx.cookies.get('token'),
            domain: 'localhost',
            path: '/',
          },
          {
            name: 'start_day',
            value: start_day,
            domain: 'localhost',
            path: '/',
          },
        ];
        const retPdf = await generatePdf(pdfPreName, dateTime, cookies)
          .then((pdfname) => {
            ctx.body = {
              result_code: 0,
              url: `${pdfname}${dateTime}.pdf`,
              state: 'success',
            };
          });
        console.log('数据', retPdf);
      }
    
      async downloadPdf(ctx) {
        const fileName = ctx.query.name;
        const filePath = path.resolve(`${__dirname}`, `../../${fileName}`);
        ctx.attachment(filePath);
        await send(ctx, fileName);
        setTimeout(() => {
          fs.unlink(fileName, (err) => {
            console.log('文件已被删除');
          });
        }, 15000);
      }
    }
    
    

    fix1: 开始的时候生成的pdf都是只有一页,并且生成的是从页面左上角开始的,并不是我们想要的。经过多番修改,发现是因为页面的外部元素含有min-height min-width等样式导致页面的宽高出现问题。
    fix2: 项目部署,包安装问题,puppeteer依赖的包无法安装,需要sre同学支持安装。
    fix3: 字体问题,需要sre同学协助安装字体,或者引动通过cdn方式引用字体包
    fix4: 导出pdf部分样式问题,兼容处理。
    fix4: 无头浏览器登录问题,cookie值的传递

    相关文章

      网友评论

          本文标题:前端生成pdf解决方案

          本文链接:https://www.haomeiwen.com/subject/crzjhrtx.html