赞
踩
最近遇到开发需求,需要将SpringBoot后端的一个H5网页,转换为图片,并发送到指定的接口上。由于要考虑到要兼容各种CSS样式和AJAX请求的因素,因此用内嵌浏览器的实现方法往往会导致网页样式出不来。
因此思路是操作服务器本地的Chrome,访问网页再“截图”为图片。然而Java本身并没有合适的控制本地Chrome的API,而nodejs的puppeteer提供了可以控制headless chrome的API接口,其中就包括对网页进行截屏的API。因此,决定用Java控制nodejs完成网页转图片文件的操作,再用Java读取转换完的图片文件进行操作。
这里以ubuntu服务器为例说明步骤如下:
cnpm i puppeteer-core
apt-get install fontforge
然后用fontforge将ttc另存为ttf,并将文件copy到服务器的/usr/share/fonts/truetype/xxx,其中xxx文件夹可以自行创建,然后运行以下命令,系统可以自动检测/usr/share/fonts及子目录下的ttf文件,刷新字体缓存。
sudo mkfontscale
sudo mkfontdir
sudo fc-cache -fv
fc-list
const puppeteer = require('puppeteer-core'); const os = require('os'); function timeout(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }; (async () => { const browser = await puppeteer.launch({ headless: true, executablePath: process.argv[2] }); const page = await browser.newPage(); await page.goto(process.argv[3]); // 仅指定宽度,高度根据返回结果确定 await page.setViewport({width:parseInt(process.argv[4]), height:0}); // 等待页面自动执行的AJAX await timeout(10000); const pagesize = await page.evaluate(() => { // TODO: 在这里可以动态操作dom元素 return { width: document.documentElement.scrollWidth, height: document.documentElement.scrollHeight}; }); // 重新调整viewport大小,适配真实的页面 await page.setViewport({width:pagesize.width, height:pagesize.height}); const path = process.argv[5].replace('~',os.homedir()); await page.screenshot({path: path, fullpage: true}); console.log("file " + path + " is saved, page size:" + pagesize.width + "," + pagesize.height); await browser.close(); })();
protected String generateScreenShot(Properties prop) throws Exception { log.info("generate screenshot"); ProcessBuilder pb = new ProcessBuilder("node", // 不指定这个参数的话,nodejs执行出现unhandled exception时,会block进程 "--unhandled-rejections=strict", prop.getProperty("alert_screenshot_script_file"), prop.getProperty("alert_screenshot_browser"), prop.getProperty("alert_detail_url"), prop.getProperty("alert_screenshot_width"), prop.getProperty("alert_screenshot_file")); pb.directory(ResourceUtils.getFile("classpath:" + prop.getProperty("alert_screenshot_script_path"))); // 将正常的output和error分开显示 pb.redirectErrorStream(false); Process p = pb.start(); // 为了支持分开显示,采用线程的方法,同时防止read()之类的操作阻塞主线程 Thread t1 = printScreenShotInformation(p.getInputStream(), false); Thread t2 = printScreenShotInformation(p.getErrorStream(), true); // 等待执行 int exitcode = p.waitFor(); Thread.sleep(1000); // 如果有线程有read()没返回,可以强制退出 t1.interrupt(); t2.interrupt(); String strFilePath = prop.getProperty("alert_screenshot_file").replaceFirst("^~", System.getProperty("user.home")); log.info(String.format("fininsh saving a screen shot to %s with exit code:%d", strFilePath, exitcode)); return strFilePath; } protected Thread printScreenShotInformation(final InputStream in, boolean bIsErr) { Thread t = new Thread() { @Override public void run() { try { int n; StringBuilder sb = new StringBuilder(); while ((n = in.read()) != -1) { char c = (char)n; if(c == '\r' || c == '\n') { if(sb.length() > 0) { if(bIsErr) { log.error(sb.toString()); } else { log.info(sb.toString()); } sb = new StringBuilder(); } } else { sb.append(c); } } if(sb.length() > 0) { if(bIsErr) { log.error(sb.toString()); } else { log.info(sb.toString()); } } } catch(Exception e) { e.printStackTrace(); } } }; t.start(); return t; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。