需求
后端批量导出一些个人评价报告的PDF,要和前端页面展示的报告格式一样。几个点:
1)渲染页面数据;
2) 后端需要渲染echarts图片;
3)需要导出PDF
解决方案
使用thymeleaf模板+phantomjs
思路是提供一个接口,用thymeleaf模板渲染数据,然后利用phantomjs 访问这个接口,将页面渲染成pdf,但是这样批量导出的效率不知好不好
使用thymeleaf生成静态文件,发给phantomjs渲染
这样需要设置好js的路径,需要额外生成文件发给phantomjs渲染,类似下面的例子
public class EchartsConvertUtils {
// thymeleaf 渲染 html 引擎
private final static TemplateEngine templateEngine = new TemplateEngine();
// 无头浏览器驱动
private static PhantomJSDriver webDriver;
static {
try {
webDriver = getPhantomJs();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 使用 Thymeleaf 渲染 HTML
* @param template HTML模板
* @param params 参数
* @return 渲染后的HTML
*/
public static String render(String template, Map params){
Context context = new Context();
context.setVariables(params);
return templateEngine.process(template, context);
}
/**
* 获取字符串格式的模板
* @param filePath
* @return
*/
public static String getHtmlString(String filePath){
return FileUtil.readString(filePath,"UTF-8");
}
/**
* 获取 无界面浏览器路径,这里需要区分linux 还是 windows
* @return
*/
private static String getPhantomJsPath() {
String path = ""; //System.getProperty("phantomjs.binary.path");
if (path == null || "".endsWith(path)) {
return "/usr/bin/phantomjs";
} else {
return path;
}
}
private static PhantomJSDriver getPhantomJs() {
//设置必要参数
DesiredCapabilities dcaps = new DesiredCapabilities();
//ssl证书支持
dcaps.setCapability("acceptSslCerts", true);
//截屏支持
dcaps.setCapability("takesScreenshot", true);
//css搜索支持
dcaps.setCapability("cssSelectorsEnabled", true);
//js支持
dcaps.setJavascriptEnabled(true);
//驱动支持(第二参数表明的是你的phantomjs引擎所在的路径,which/whereis phantomjs可以查看)
// fixme 这里写了执行, 可以考虑判断系统是否有安装,并获取对应的路径 or 开放出来指定路径
dcaps.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY, getPhantomJsPath());
//创建无界面浏览器对象
return new PhantomJSDriver(dcaps);
}
/**
* 将html 渲染为图片文件
* @param url
* @return
* @throws IOException
*/
public static BufferedImage renderHtml2Image(String url) throws IOException {
if (webDriver != null) {
webDriver.get(url);
File file = webDriver.getScreenshotAs(OutputType.FILE);
return ImageIO.read(file);
} else {
return null;
}
}
/**
* 将图片流写入到图片文件
* @param bi
* @throws IOException
*/
public static void writeImageFile(BufferedImage bi,String filePath) throws IOException {
File outputfile = new File(filePath+"saved.png");
ImageIO.write(bi, "png", outputfile);
}
public static String getProjectPath(){
String path = ClassUtils.getDefaultClassLoader().getResource("").getPath();
return path+"/templates/";
}
/**
* 以下是整个流程
*/
public static void echartsConvert(String templatePath,Map params,String filePath){
// 首先加载模板引擎进行渲染
String html=render(getHtmlString(templatePath),params);
// 将html写入文件,要必去能和js文件放到一起,这样才嫩保证PhantomJS 加载html 的时候找到 js
String filename=filePath+UUID.randomUUID().toString().substring(0, 8);
File file=new File(filename+".html");
FileUtil.writeString(html,file,"UTF-8");
BufferedImage img = null;
try {
for (int i = 0; i < 20; ++i) {
long start = System.currentTimeMillis();
img = renderHtml2Image(filename+".html");
long end = System.currentTimeMillis();
System.out.println("cost: " + (end - start));
}
writeImageFile(img,filename);
System.out.println(Base64Util.encode(img, "png"));
FileUtil.del(file);
} catch (IOException e) {
e.printStackTrace();
}
}