美文网首页
使用node生成pdf文件

使用node生成pdf文件

作者: Yinzhishan | 来源:发表于2022-08-08 15:13 被阅读0次

技术储备

  1. egg ,我们要启动一个node服务来提供接口;
  2. Puppeteer ,用来生成pdf;
  3. umi或其他前端页面框架,用来开发页面
  4. nodejs、html、css、javaScript;

技术介绍

做了一个医药项目,需求是要把患者每天的打卡记录生成为pdf文件,方便患者打印出来给带给医生看;所以就有了这个方案;
技术介绍:

  1. egg 官网;
    egg 是阿里出品的一款 node.js 后端 web 框架,基于 koa 封装,并做了一些约定。
    学习这个链接;对这个项目就够用了;
  2. Puppeteer [官网](https://pptr.dev/
    Puppeteer 是一个 Node 库,它提供了一个高级 API 来通过 DevTools 协议控制 Chromium 或 Chrome。
    Puppeteer 默认以headless 模式运行,但是可以通过修改配置文件运行“有头”模式。
  3. umi 官网
    Umi,中文发音为「乌米」,是可扩展的企业级前端应用框架。Umi 以路由为基础的,同时支持配置式路由和约定式路由,保证路由的功能完备,并以此进行功能扩展。然后配以生命周期完善的插件体系,覆盖从源码到构建产物的每个生命周期,支持各种功能扩展和业务需求。

技术实现

  1. 创建生成pdf的核心流程
'use strict';
const dayjs = require('dayjs');
const { Cluster } = require('puppeteer-cluster');
const { tools } = require('./index');

const sleep = async(time) => {
    return new Promise(resolve => {
      setTimeout(_ => {
        resolve()
      }, time)
    })
  }

const launchOptions = {
    headless: true,
    ignoreHTTPSErrors: true, // 忽略证书错误
    waitUntil: 'networkidle2',
    defaultViewport: {
        width: tools.interceptWidth,
        height: tools.interceptHeight,
        deviceScaleFactor: tools.deviceScaleFactor
    },
    userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_1_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/16C101 ios/4.10.0',
    args: [
        '--disable-gpu',
        '--disable-dev-shm-usage',
        '--disable-web-security',
        '--disable-xss-auditor', // 关闭 XSS Auditor
        '--no-zygote',
        '--no-sandbox',
        '--disable-setuid-sandbox',
        '--allow-running-insecure-content', // 允许不安全内容
        '--disable-webgl',
        '--disable-popup-blocking',
        '–no-first-run', // 没有设置首页。在启动的时候,就会打开一个空白页面。
        '–single-process' // 单进程运行
    ]
};

const pagePool = async () => {
    const cluster = await Cluster.launch({
        concurrency: Cluster.CONCURRENCY_PAGE, // 单Chrome多tab模式
        maxConcurrency: 20, // 并发的workers数
        retryLimit: 2, // 重试次数
        // skipDuplicateUrls: true, // 不爬重复的url
        // monitor: true, // 显示性能消耗
        puppeteerOptions: launchOptions,
    });
    await cluster.task(async ({ page, data: { log, url, renderData }, }) => {
        return new Promise(async (resolve, reject) => {
            let startTime = dayjs().valueOf()
            
            // 查看H5页面的控制台输出日志
            page.on('console', (msg) => {
                for (let i = 0; i < msg.args().length; ++i) {
                    // console.log(`${i}: ${msg.args()[i]}`);
                    if (`${msg.args()[i]}`.includes('RENDER')) {
                        log.send({ msg: `api/pdf, render =======> msg ${msg.args()[i]}` })
                    }

                    if (`${msg.args()[i]}`.includes('ERROR')) {
                        log.error({ msg: `api/pdf, render =======> error msg ${msg.args()[i]}` })
                    }
                }
            });
            try {

                log.send({
                    msg: 'api/pdf, puppeteer launchs',
                    data: {
                        launchs: true
                    }
                })

                // 向H5页面注入数据
                const dataString = JSON.stringify(renderData)
                page.evaluateOnNewDocument(`
                    Object.defineProperty(window, 'renderData', {
                    get() {
                        return ${dataString}
                    }
                    })
                `)

                await page.goto(url)
                log.send({ msg: `api/pdf, page go to ${url} use ${dayjs().valueOf() - startTime} ms` })

                startTime = dayjs().valueOf()
                await page.waitForFunction(() => {
                    console.log(`RENDER: ======> from waitForFunction ${window.renderData}`)
                    console.log(`RENDER: ${window.routerBase}, path: ${window.location.pathname}`)

                    if (!window.routerBase) return true

                    return window.isRenderDone == true
                }, { polling: 500, timeout: 3 * 1000 })

                log.send({ msg: `api/pdf, page render use ${dayjs().valueOf() - startTime} ms` })

                startTime = dayjs().valueOf()

                // 处理首页没有页首
                await page.addStyleTag({
                    content: `
                        @page:first { margin-top: 0; }
                    `,
                });

                const buffer = await page.pdf({
                    width: tools.paperWidth,
                    height: tools.paperHeight,
                    headerTemplate: `
                        <div style="color: rgb(18, 25, 36); width: 100%; border-bottom: 1px solid #121924; padding-right: 16px; font-family: PingFangSC-Medium, PingFang SC; display: inline-block; line-height: 15px; padding-bottom: 7px; margin-top: -7px;">
                        <span style="font-size: 14px; font-weight: 500; float: left;">PDF头部</span>

                        <span style="font-size: 10px; float: right; magrin-right: 10px; ">${dayjs().format('YYYY-MM-DD hh:mm')}</span>
                        <strong style="font-size: 10px; float: right; ">生成时间:</strong>
                        </div>
                        `,
                    displayHeaderFooter: true,
                    printBackground: true,
                    omitBackground: true,
                    preferCSSPageSize: true,
                    margin: {
                        top: '50px',
                        bottom: '50px',
                        left: 0,
                        right: 0,
                    },
                    footerTemplate: `<div style=" font-size: 10px; padding-top: 5px; text-align: center; width: 100%;
                  background:transparent !important; background-color:rgba(0,0,0,0) !important; font-family: PingFangSC-Medium, PingFang SC;">
                  第<span class="pageNumber"></span>/<span class="totalPages"></span>页
            </div>`
                })
                resolve({ buffer, ext: 'pdf' })
            } catch (e) {
                log.error({
                    msg: 'api/pdf, htmlToPDF function error', error: e,
                })
                reject(e)
            }
        });
    });
    return cluster;
}

module.exports = pagePool;

  1. 因保密限制,具体的H5代码和egg代码就不再提供,按照常规实现即可;

相关文章

  • 使用node生成pdf文件

    技术储备 egg ,我们要启动一个node服务来提供接口; Puppeteer ,用来生成pdf; umi或其他前...

  • windows+PHP+shell_exec()无法执行的原因

    今天使用shell_exec()命令执行wkhtmltopdf.exe 生成pdf文件时,总是无法生成PDF文件,...

  • PDF

    iOS生成PDF图文ios开发之--PDF文件生成 iOS11 PDFKit 使用例程

  • GitBook 安装配置 & 导出PDF

    主要思路 安装 GitBook 终端生成HTML 使用calibre插件生成PDF 主要流程 安装node.js ...

  • JS下载PDF文件

    由于需求需要将数据下载为PDF文件,由于性能还有工时考量没有使用后端生成PDF,而是采用js下载PDF文件, 使用...

  • vue 日常使用

    一. vue 使用 pdf 插件 使用Vue Cli生成项目 下载pdf.js 解压下载后的文件,将文件夹里面的b...

  • 在线考试系统之Excel导入导出

    1. 使用 node-xlsx 包 node-xlsx: 基于Node.js解析excel文件数据及生成excel...

  • TCPDF - 生成PDF文件太大 (大于9M)

    使用TCPDF生成 pdf 文件太大 - 7页 基本在 10M左右 原因分析定位 开始以为主要是PDF中的图片文件...

  • markdown文档生成快捷目录

    1. 安装node插件 2.使用 文件内容 使用命令 生成文件 如此既可在github上使用目录查看了

  • 移动端脚手架

    目录结构 使用指南 npm install //安装node依赖以及开发依赖 生成node_modules文件夹...

网友评论

      本文标题:使用node生成pdf文件

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