Java端网页PDF生成方案(Chrome无头浏览器)

您所在的位置:网站首页 如何将html转为PDF Java端网页PDF生成方案(Chrome无头浏览器)

Java端网页PDF生成方案(Chrome无头浏览器)

2024-05-24 16:03| 来源: 网络整理| 查看: 265

1. 背景

目前正在进行得一个小程序,客户提出了需求,要求将部分数据生成PDF提供下载和分享,由于是小程序项目,所以PDF得生成只能通过后端来进行,于是采用了常规的itext方案。生成得是那种比较偏文字类型得PDF。生成得PDF截图如下图所示

虽然也能通过部分手段实现报表的插入,但是在使用一段时间后,客户对PDF得美观度提出了需求,给出了设计稿,最终实现效果如下图所示

这时候itext就无法解决这一问题了,因为很多内容需要定制化,并且插入了很多图片和echarts图表。因此需要寻求其他得解决方案。

2. 解决方案

核心思路,通过H5生成想要实现得页面,截取该页面,后端通过切割该图片由itext贴入PDF中,最终生成对应得PDF。

1. 小程序内嵌H5页面,通过canvas绘制生成图片传递到后台,并生成PDF。

小程序通过web-view内嵌H5页面,通过访问对应得页面绘制图片,并传到后台生成PDF,过程并不顺利,因为安卓和ios浏览器内核得不同,导致绘制得页面出现偏移以及部分细节丢失等各种问题,最后生成得PDF有大概率无法使用,也有部分时候能够成功生成,因此放弃了这个方案。

2. 纯JAVA端生成。

通过ChormeHeadless + selenium实现在服务器访问网页并截图。

Java端需要引入一下几个jar包 com.google.guava guava 30.1.1-jre org.seleniumhq.selenium selenium-java 4.0.0-alpha-6

服务器或者本地安装chrome以及对应得驱动

chrome下载地址

驱动下载地址

ps:注意版本得对应

Java实现/** * 通用chromeDriver获取方法 * * @param argument 获取浏览器宽高 */ public WebDriver getDriver(String argument) { //驱动地址(linux用) System.setProperty("webdriver.chrome.driver", "/xxx/chromedriver"); ChromeOptions chromeOptions = new ChromeOptions(); // chrome安装路径(linux用) chromeOptions.setBinary("/usr/bin/google-chrome"); chromeOptions.addArguments("--no-sandbox"); chromeOptions.addArguments("start-maximized"); chromeOptions.addArguments("disable-infobars"); chromeOptions.addArguments("--disable-dev-shm-usage"); chromeOptions.addArguments("--test-type"); chromeOptions.addArguments("--disable-extensions"); chromeOptions.addArguments("--headless"); chromeOptions.setExperimentalOption("useAutomationExtension", false); chromeOptions.addArguments("--disable-dev-shm-usage"); if(!argument.equals("")) chromeOptions.addArguments(argument); WebDriver webDriver = new ChromeDriver(chromeOptions); return webDriver; } /** * 无头浏览器获取页面生成的宽高 * 完成后执行截图 */ public static pdfImgGenerate(String imgName, String type, String resultId) { String url = MdisConfig.getPdfUrl() + "?id=" + resultId + "&type=" + type; WebDriver driver = new PdfUtils().getDriver(""); driver.get(url); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //获取JS执行器,可以执行js代码来控制页面 JavascriptExecutor driver_js = ((JavascriptExecutor) driver); Long height = (Long) driver_js.executeScript("return document.body.scrollHeight"); Long width = (Long) driver_js.executeScript("return document.body.scrollWidth"); System.out.println("height" + height); Map map = new HashMap(); map.put("height", height); map.put("width", width); driver.quit(); new PdfUtils().frontEndCut(imgName, url, map); } /** * 无头浏览器截图 */ public frontEndCut(String imgName, String url, Map map) { String argument = "--window-size=" + map.get("width") + "," + map.get("height"); WebDriver driver = new PdfUtils().getDriver(argument); driver.get(url); // 页面等待渲染时长,如果你的页面需要动态渲染数据的话一定要留出页面渲染的时间,单位默认是秒 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } // 获取到截图的文件 File screenshotFile = ((TakesScreenshot) driver) .getScreenshotAs(OutputType.FILE); if((screenshotFile != null) && screenshotFile.exists()) { //截取到的图片存到本地 FileOutputStream out = null; FileInputStream in = null; try { in = new FileInputStream(screenshotFile); // 本地路径 out = new FileOutputStream(MdisConfig.getUploadPath() + "/images/" + imgName + ".png"); byte[] b = new byte[1024]; while(true) { int temp = in.read(b, 0, b.length); // 如果temp = -1的时候,说明读取完毕 if(temp == -1) { break; } out.write(b, 0, temp); } } catch (Exception e) { //TODO异常处理 } } driver.quit(); }

由于比较赶时间,代码写的比较粗糙,总体得实现思路是,调用服务器得浏览器访问要被截图得页面,获取整个页面对应得高度和宽度,然后通过获得得宽高再对该区域进行一次截图保存到本地。 获取到该图片后,即可对图片根据A4纸大小进行切割并通过itext贴入PDF,切割和生成PDF比较简单,代码就不贴了。由于该页面是VUE编写而非静态页面,因此截图及获取高度时需要等待一段时间,等待页面绘制完全。

3. 可能遇到得问题1. 本地运行报错

解决方法:

查看chrome版本是否与驱动对应 是否赋予驱动对应得运行权限 查看selenium版本是否与chrome版本得年代一致,修改selenium版本2. 部署到服务器运行报错

解决方法:

是否赋予驱动对应得运行权限 查看本地代码得chrome-headless参数是否正确,特别注意'--no-sandbox'这个参数3. 其他错误(更换selenium版本后,jar包中得版本并没有被替换)

解决方法:

移除maven中引入得swagger包,可能存在冲突4. 总结

经过多方尝试,最终终于实现了我想要得效果,在寻找解决方案得过程中,遇到了无数得问题,最终把坑踩掉了,写篇文章记录一下。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3