需求
服务端后台生成spreadjs表格控件的缩略图。
思路
- 获取spreadjs显示的json数据
- 使用spreadjs库还原显示数据
- 对还原之后的数据生成缩略图
解决方法
使用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