使用puppeteer生成spreadjs缩略图

需求

服务端后台生成spreadjs表格控件的缩略图。

思路

  1. 获取spreadjs显示的json数据
  2. 使用spreadjs库还原显示数据
  3. 对还原之后的数据生成缩略图

解决方法

使用puppeteer提供的screenshot方法导出。

遇到的问题

puppeteer提供两个方法设置html内容,setContent和goto。

goto的用法有这几种:

  • 直接设置url,例如:
page.goto('https://www.baidu.com');
  • 加载本地html文件
page.goto(`file:///${htmlPath}`);
  • 加载html内容
page.goto(`data:text/html,${htmlContent}`, {waitUntil: 'networkidle0'});

setContent用法如下:

page.setContent(html, {waitUntil: 'load'});

为减少网络延时,直接使用了加载本地文件。所以直接加载url方式被抛弃。所以采用了加载本地html文件的方式。

因为html模板文件里的部分值需要动态填充,所以最终在goto方式加载html内容和setContent两种方式之间选择。
但是这两种方式都遇到问题,无法使用本地js文件。html代码如下:

<!doctype html>
<html style="height:100%;font-size:14px;">

<head>
    <meta name="预览" content="zh-cn"/>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    
    <link rel="stylesheet" type="text/css" href="styles.css"/>
    <link rel="stylesheet" href="css/gc.spread.sheets.excel2013white.13.0.0.css" title="spread-theme"/>
    <script type="text/javascript" src="scripts/jquery-1.11.1.min.js"></script>
    <script type="text/javascript" src="scripts/gc.spread.sheets.all.13.0.0.min.js"></script>
    <script type="text/javascript" src="scripts/plugins/gc.spread.sheets.shapes.13.0.0.min.js"></script>
    <script type="text/javascript" src="scripts/plugins/gc.spread.sheets.charts.13.0.0.min.js"></script>
    <script type="text/javascript" src="scripts/plugins/gc.spread.sheets.print.13.0.0.min.js"></script>
    <script type="text/javascript" src="scripts/plugins/gc.spread.sheets.barcode.13.0.0.min.js"></script>
    <script type="text/javascript" src="scripts/interop/gc.spread.excelio.13.0.0.min.js"></script>
    <script type="text/javascript" src="scripts/resources/zh/gc.spread.sheets.resources.zh.13.0.0.min.js"></script>

    <style type="text/css">
        .excel-data-container {
            position: relative;
            height: 100%;
            overflow: hidden;
        }

        .yz-watermark {
            border-width: 3px;
            border-style: solid;
            border-color: #008000;
            border-radius: 8px;
            color: #008000;
            opacity: 0.6;
            position: absolute;
            z-index: 1;
            left: 40%;
            top: 30%;
            font-size: 30pt;
            -webkit-transform: rotate(-45deg);
            -ms-transform: rotate(-45deg);
            transform: rotate(-45deg);
            font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;
        }

        body {
            position: absolute;
            top: 0;
            bottom: 0;
            left: 0;
            right: 0;
        }
    </style>

    <script>
        GC.Spread.Sheets.LicenseKey = "xxxxxx";

        window.onload = function () {
            var spread = new GC.Spread.Sheets.Workbook(document.getElementById('ss'));
            spread.fromJSON(sheetJson);
        };
    </script>
</head>

<body>
<div class="yz-watermark">
    xx科技
</div>

<div class="excel-data-container">
    <div id="ss" style="width:100%;height:100%"></div>
</div>
</body>
</html>

html中引用的css文件和js文件均不能正常渲染。所以spreadjs根本无法正常显示。

查了很多方式,有的说设置arg属性:args: [‘–no-sandbox’, ‘–disable-setuid-sandbox’],但是并没有效果。由于goto方法加载本地文件的方式可以显示正常,代码如下。所以去看了下puppeteer源码,是否可以在外部设置一些goto的环境给setContent用。然而,源码里goto和setContent使用的是两个不同的类。处理方式差别还是挺大的。所以此路不通。

可正常运行的goto方式代码:

    const browser = await puppeteer.launch();
    const page = await browser.newPage();

    await page.goto(`file:///F:\\test\\test-node-html-to-image\\index.html`);
    await page.screenshot({
        path: 'example' + index + '.png',
        fullPage: true,
    });

    await browser.close();

最终参考个issues 949里的思路,找到了解决方案。

在html文件中不写加载外部文件js和css文件的代码,而是用puppeteer提供的addScriptTag和addStyleTag两个方法在运行时加载,代码如下:

html文件

<!doctype html>
<html style="height:100%;font-size:14px;">

<head>
    <meta name="预览" content="zh-cn"/>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>

    <style type="text/css">
        .excel-data-container {
            position: relative;
            height: 100%;
            overflow: hidden;
        }

        .yz-watermark {
            border-width: 3px;
            border-style: solid;
            border-color: #008000;
            border-radius: 8px;
            color: #008000;
            opacity: 0.6;
            position: absolute;
            z-index: 1;
            left: 40%;
            top: 30%;
            font-size: 30pt;
            -webkit-transform: rotate(-45deg);
            -ms-transform: rotate(-45deg);
            transform: rotate(-45deg);
            font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;
        }

        body {
            position: absolute;
            top: 0;
            bottom: 0;
            left: 0;
            right: 0;
        }
    </style>

    <script>
        GC.Spread.Sheets.LicenseKey = "";

        window.onload = function () {
            var spread = new GC.Spread.Sheets.Workbook(document.getElementById('ss'));
            spread.fromJSON(sheetJson);
        };
    </script>
</head>

<body>
<div class="yz-watermark">
    xx科技
</div>

<div class="excel-data-container">
    <div id="ss" style="width:100%;height:100%"></div>
</div>
</body>
</html>

js文件

const puppeteer = require('puppeteer');
const fs = require('fs');
const path = require('path');
const os = require('os');
const {shortId} = require('yz-utilities');

const scripts = [
    path.join(__dirname, 'scripts/jquery-1.11.1.min.js'),
    path.join(__dirname, 'scripts/gc.spread.sheets.all.13.0.0.min.js'),
    path.join(__dirname, 'scripts/plugins/gc.spread.sheets.shapes.13.0.0.min.js'),
    path.join(__dirname, 'scripts/plugins/gc.spread.sheets.charts.13.0.0.min.js'),
    path.join(__dirname, 'scripts/plugins/gc.spread.sheets.print.13.0.0.min.js'),
    path.join(__dirname, 'scripts/plugins/gc.spread.sheets.barcode.13.0.0.min.js'),
    path.join(__dirname, 'scripts/interop/gc.spread.excelio.13.0.0.min.js'),
    path.join(__dirname, 'scripts/resources/zh/gc.spread.sheets.resources.zh.13.0.0.min.js')
];

const stylesheets = [
    path.join(__dirname, 'css/gc.spread.sheets.excel2013white.13.0.0.css')
];

const templatePath = path.join(__dirname, 'template/thumbnail.html');
const template = fs.readFileSync(templatePath).toString('utf8')


module.exports = async (sheetJson) => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();

    for (const script of scripts) {
        await page.addScriptTag({path: script})
    }

    for (const stylesheet of stylesheets) {
        await page.addStyleTag({path: stylesheet})
    }

    const finalSheetJsonData = template.replace('sheetJson', sheetJson);
    await page.setContent(finalSheetJsonData, {waitUntil: 'load'});
    const thumbnailPath = path.join(os.tmpdir(), shortId() + '.png');
    await page.screenshot({
        path: thumbnailPath,
        fullPage: true,
        // clip:{
        //     x: 35,
        //     y: 20,
        //     width: 150,
        //     height: 200
        // }
    });
    await browser.close();
    return thumbnailPath;
}

其他

查资料过程中还发现一个强大的库,wkhtmltopdf。但是无法正确导出spreadjs的pdf文件。

ubuntu下的问题和解决

ubuntu下报错

(node:433) UnhandledPromiseRejectionWarning: Error: Failed to launch the browser process!
/app/node_modules/puppeteer/.local-chromium/linux-782078/chrome-linux/chrome: error while loading shared libraries: libgobject-2.0.so.0: cannot open shared object file: No such file or directory


TROUBLESHOOTING: https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md

    at onClose (/app/node_modules/puppeteer/lib/cjs/puppeteer/node/BrowserRunner.js:193:20)
    at Interface.<anonymous> (/app/node_modules/puppeteer/lib/cjs/puppeteer/node/BrowserRunner.js:183:68)
    at Interface.emit (events.js:327:22)
    at Interface.close (readline.js:416:8)
    at Socket.onend (readline.js:194:10)
    at Socket.emit (events.js:327:22)
    at endReadableNT (_stream_readable.js:1220:12)
    at processTicksAndRejections (internal/process/task_queues.js:84:21)
(node:433) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:433) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

原因是缺少chrome的依赖库,查看chrome依赖项

 ldd chrome | more

会发现有很多缺失项

linux-vdso.so.1 => (0x00007ffcd3ee4000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f55baf90000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f55bad8c000)
librt.so.1 => /lib64/librt.so.1 (0x00007f55bab83000)
libpangocairo-1.0.so.0 => not found
libpango-1.0.so.0 => not found
libcairo.so.2 => not found
libgobject-2.0.so.0 => /lib64/libgobject-2.0.so.0 (0x00007f55ba932000)
libglib-2.0.so.0 => /lib64/libglib-2.0.so.0 (0x00007f55ba5fb000)
libX11.so.6 => /lib64/libX11.so.6 (0x00007f55ba2bc000)
libX11-xcb.so.1 => /lib64/libX11-xcb.so.1 (0x00007f55ba0ba000)
libxcb.so.1 => /lib64/libxcb.so.1 (0x00007f55b9e98000)
libXcomposite.so.1 => not found
libXcursor.so.1 => not found
libXdamage.so.1 => not found
libXext.so.6 => not found
libXfixes.so.3 => not found
libXi.so.6 => not found
libXrender.so.1 => not found
libXtst.so.6 => not found
libgmodule-2.0.so.0 => /lib64/libgmodule-2.0.so.0 (0x00007f55b9c91000)
libnss3.so => /lib64/libnss3.so (0x00007f55b9966000)
libnssutil3.so => /lib64/libnssutil3.so (0x00007f55b9739000)
libsmime3.so => /lib64/libsmime3.so (0x00007f55b9512000)
libnspr4.so => /lib64/libnspr4.so (0x00007f55b92d3000)
libcups.so.2 => not found
libexpat.so.1 => /lib64/libexpat.so.1 (0x00007f55b90a9000)
libfontconfig.so.1 => /lib64/libfontconfig.so.1 (0x00007f55b8e6b000)
libdbus-1.so.3 => /lib64/libdbus-1.so.3 (0x00007f55b8c23000)
libXss.so.1 => not found
libXrandr.so.2 => not found
libgconf-2.so.4 => not found
libgio-2.0.so.0 => /lib64/libgio-2.0.so.0 (0x00007f55b88a2000)
libasound.so.2 => /lib64/libasound.so.2 (0x00007f55b85a9000)
libm.so.6 => /lib64/libm.so.6 (0x00007f55b82a7000)
libatk-1.0.so.0 => not found
libgtk-3.so.0 => not found
libgdk-3.so.0 => not found
libgdk_pixbuf-2.0.so.0 => not found
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f55b808f000)
libc.so.6 => /lib64/libc.so.6 (0x00007f55b7cce000)
/lib64/ld-linux-x86-64.so.2 (0x000055a8c8793000)
libffi.so.6 => /lib64/libffi.so.6 (0x00007f55b7ac6000)
libXau.so.6 => /lib64/libXau.so.6 (0x00007f55b78c1000)
libplc4.so => /lib64/libplc4.so (0x00007f55b76bc000)
libplds4.so => /lib64/libplds4.so (0x00007f55b74b8000)
libfreetype.so.6 => /lib64/libfreetype.so.6 (0x00007f55b7211000)
libz.so.1 => /lib64/libz.so.1 (0x00007f55b6ffb000)
libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f55b6dd3000)
libresolv.so.2 => /lib64/libresolv.so.2 (0x00007f55b6bb9000)
libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f55b6957000)

使用 npm i puppeteer 安装puppeteer之后还需要安装chrome依赖环境。参考bug3443

apt install gconf-service libasound2 libatk1.0-0 libatk-bridge2.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget  libgbm-dev language-pack-zh-hans fonts-droid-fallback ttf-wqy-zenhei ttf-wqy-microhei fonts-arphic-ukai fonts-arphic-uming

由于表格中会出现中文,所以需要安装中文包和对应语言,参考Ubuntu的中文乱码问题

修改/etc/environment(在文件的末尾追加):
LANG="zh_CN.UTF-8"
LANGUAGE="zh_CN:zh:en_US:en"

再修改/var/lib/locales/supported.d/local(没有这个文件就新建,同样在末尾追加):
en_US.UTF-8 UTF-8
zh_CN.UTF-8 UTF-8
zh_CN.GBK GBK
zh_CN GB2312

最后,执行命令:
sudo locale-gen

libXrandr.so.2 => not found

参考

Apt in the official docker ubuntu image and also in phusion/baseimage is configured only with support for the amd64. This is what it tells you if you ask:
# dpkg --print-architecture
amd64
# dpkg --print-foreign-architectures
<no output>
The way to fix this is to add another architecture before the apt-get update:
# dpkg --add-architecture i386
# apt-get update
# apt install libxrandr2:i386

由于64位架构问题导致库安装不上去。

参考

  • https://github.com/puppeteer/puppeteer/issues/949
  • https://html.developreference.com/article/10534022/Could+not+render+images+in+PDF+(Puppeteer)
  • https://stackoverflow.com/questions/47587352/opening-local-html-file-using-puppeteer
  • https://github.com/puppeteer/puppeteer/issues/2913
  • https://handlebarsjs.com/guide/expressions.html#html-escaping
  • https://github.com/puppeteer/puppeteer
  • https://github.com/chuongtrh/html_to_pdf
  • https://handlebarsjs.com/

ubuntu服务器下问题解决参考:

  • https://github.com/puppeteer/puppeteer/issues/391
  • https://github.com/puppeteer/puppeteer/issues/3443
  • https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值