Java实现HTML页面截图功能

article/2025/7/21 12:32:21

概述

业务开发中,经常会有HTML页面截图,或打印另存为PDF文件的需求。本文即是HTML页面截图需求的技术调研过程的成文。不想看长篇大论的同学,可以直接看Selenium部分,本人最后也是采取此方案。

html2canvas

直接上代码:

const canvas = function () {html2canvas($("#chart"), {onrendered: function (canvas) {// 将id为“class11”部分的代码转换为canvas$("#class11").html(canvas);const type = 'png';// 将图片转换为png类型的流const imgData = canvas.toDataURL('png');const saveFile = function (data, filename) {const save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');save_link.href = data;save_link.download = filename;const event = document.createEvent('MouseEvents');event.initMouseEvent('click', true, false, window, 0, 0, 0,0, 0, false, false, false, false, 0, null);save_link.dispatchEvent(event);};const filename = 'html2canvasSavePic' + (new Date()).getTime() + '.' + type;// 下载文件saveFile(imgData, filename);}});
};

优缺点

html2canvas可以将HTML代码块进行截取,并生成快照形式的canvas,然后利用html5的下载功能。
优点:前台技术,实现比较容易。
缺点:如果是使用H5,则只能在IE9+的版本上使用。需引用jQuery.js和html2canvas.js。

PhantomJS

PhantomJS下载地址
直接上代码:

/*** 图片保存目录*/
private static final String TEMP_PATH = "E:\\tmp\\img";private static final String BLANK = " ";/*** 可执行文件路径*/
private static final String BIN_PATH = "D:\\phantomjs-2.1.1-windows\\bin\\phantomjs.exe";/*** js路径*/
private static final String JS_PATH = "D:\\phantomjs-2.1.1-windows\\examples\\rasterize.js";public static void main(String[] args) throws IOException, InterruptedException, SAXException {String url = "http://www.samr.gov.cn/ggjgs/sjdt/gzdt/202001/t20200115_310511.html";
//  String url = "http://www.cqn.com.cn/cj/content/2018-06/12/content_5907410.htm";printUrlScreen2jpg(url);
}public static void printUrlScreen2jpg(String url) throws IOException, InterruptedException {// 图片路径String imagePath = TEMP_PATH + "/" + System.currentTimeMillis() + ".png";// Java中使用Runtime和Process类运行外部程序Process process = Runtime.getRuntime().exec(cmd(imagePath, url));Thread.sleep(1000);InputStream inputStream = process.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));while (reader.readLine() != null) {close(process, reader);}
}/*** 执行cmd命令*/
private static String cmd(String imagePath, String url) {return BIN_PATH + BLANK + JS_PATH + BLANK + url + BLANK + imagePath;
}/*** 关闭命令*/
private static void close(Process process, BufferedReader bufferedReader) throws IOException {if (bufferedReader != null) {bufferedReader.close();}if (process != null) {process.destroy();}
}

优缺点

优点:效率高,截图清晰,最重要的是能够截完整的页面,而不是当前可见页面。
缺点:对于运行环境有小小的要求,需要有PhantomJS可执行环境;很容易解决,算不上缺点。
但是,对于部分网站,如上面代码片段里面的http://www.samr.gov.cn/ggjgs/sjdt/gzdt/202001/t20200115_310511.html,截图失败:
在这里插入图片描述
很明显,被拦截。
解决思路:添加User-Agent信息,考虑在rasterize.js脚本追加,但是看不到相关的代码片段。有待进一步调研。

DjNativeSwing

原生Java实现,几乎不会被网站屏蔽,至少我没碰到过。
Maven引入依赖,第三个是64位操作系统需要添加:

<dependency><groupId>com.hynnet</groupId><artifactId>DJNativeSwing</artifactId><version>1.0.0</version>
</dependency>
<dependency><groupId>com.hynnet</groupId><artifactId>DJNativeSwing-SWT</artifactId><version>1.0.0</version>
</dependency>
<dependency><groupId>org.eclipse.swt.org.eclipse.swt.win32.win32.x86_64.4.3.swt</groupId><artifactId>org.eclipse.swt.win32.win32.x86_64</artifactId><version>4.3</version>
</dependency>

直接上代码:

public class PicSnapshotUtils extends JPanel {/*** 行分隔符*/private final static String LS = System.getProperty("line.separator", "/n");/*** 当网页超出目标大小时 截取*/private static final int MAX_WIDTH = 2000;private static final int MAX_HEIGHT = 1400;public static void main(String[] args) throws IOException, InterruptedException, SAXException {String url = "http://www.samr.gov.cn/ggjgs/sjdt/gzdt/202001/t20200115_310511.html";printUrlScreen2jpg("1122.jpg", url, 1400, 900);}/*** @param file 预生成的图片全路径* @param url  网页地址*/public PicSnapshotUtils(final String file, final String url, final String withResult) {super(new BorderLayout());JPanel webBrowserPanel = new JPanel(new BorderLayout());final JWebBrowser webBrowser = new JWebBrowser((NSOption) null);webBrowser.setBarsVisible(false);webBrowser.navigate(url);webBrowserPanel.add(webBrowser, BorderLayout.CENTER);add(webBrowserPanel, BorderLayout.CENTER);JPanel panel = new JPanel(new FlowLayout());webBrowser.addWebBrowserListener(new WebBrowserAdapter() {// 监听加载进度@Overridepublic void loadingProgressChanged(WebBrowserEvent e) {// 当加载完毕时if (e.getWebBrowser().getLoadingProgress() == 100) {String result = (String) webBrowser.executeJavascriptWithResult(withResult);if (StrUtil.isEmpty(result)) {return;}int index = result.indexOf(":");NativeComponent nativeComponent = webBrowser.getNativeComponent();Dimension originalSize = nativeComponent.getSize();Dimension imageSize = new Dimension(Integer.parseInt(result.substring(0, index)),Integer.parseInt(result.substring(index + 1)));imageSize.width = Math.max(originalSize.width, imageSize.width + 50);imageSize.height = Math.max(originalSize.height, imageSize.height + 50);nativeComponent.setSize(imageSize);BufferedImage image = new BufferedImage(imageSize.width, imageSize.height, BufferedImage.TYPE_INT_RGB);nativeComponent.paintComponent(image);nativeComponent.setSize(originalSize);// 当网页超出目标大小时if (imageSize.width > MAX_WIDTH || imageSize.height > MAX_HEIGHT) {// 截图部分图形//                        image = image.getSubimage(0, 0, MAX_WIDTH, MAX_HEIGHT);// 此部分为使用缩略图int width = image.getWidth(), height = image.getHeight();AffineTransform tx = new AffineTransform();tx.scale((double) MAX_WIDTH / width, (double) MAX_HEIGHT / height);AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);image = op.filter(image, null);}try {// 输出图像ImageIO.write(image, "jpg", new File(file));} catch (IOException ex) {ex.printStackTrace();}// 退出操作System.exit(0);}}});add(panel, BorderLayout.SOUTH);}/*** 以javascript脚本获得网页全屏后大小*/private static String getScreenWidthHeight() {return "var width = 0;" + LS +"var height = 0;" + LS +"if(document.documentElement) {" + LS +"  width = Math.max(width, document.documentElement.scrollWidth);" + LS +"  height = Math.max(height, document.documentElement.scrollHeight);" + LS +"}" + LS +"if(self.innerWidth) {" + LS +"  width = Math.max(width, self.innerWidth);" + LS +"  height = Math.max(height, self.innerHeight);" + LS +"}" + LS +"if(document.body.scrollWidth) {" + LS +"  width = Math.max(width, document.body.scrollWidth);" + LS +"  height = Math.max(height, document.body.scrollHeight);" + LS +"}" + LS +"return width + ':' + height;";}private static void printUrlScreen2jpg(final String file, final String url, final int width, final int height) {NativeInterface.open();SwingUtilities.invokeLater(() -> {String withResult = "var width = " + width + ";var height = " + height + ";return width +':' + height;";if (width == 0 || height == 0) {withResult = getScreenWidthHeight();}JFrame frame = new JFrame("网页截图");frame.getContentPane().add(new PicSnapshotUtils(file, url, withResult), BorderLayout.CENTER);// TODO:加载指定页面,最大保存为640x480的截图frame.setSize(640, 480);// 仅初始化,但不显示frame.invalidate();frame.pack();frame.setVisible(false);});NativeInterface.runEventPump();}}

优缺点

优点:大概率不会被拦截
缺点:不能截完整的页面,只能截可见(不滑动滚轮)的当前页面。Linux系统兼容性暂未测试。

另外,程序运行时,会输出报错日志,虽然截图是成功的:

Exception "java.lang.ClassNotFoundException: com/intellij/codeInsight/editorActions/FoldingData"while constructing DataFlavor for: application/x-java-jvm-local-objectref; class=com.intellij.codeInsight.editorActions.FoldingData
Exception "java.lang.ClassNotFoundException: com/intellij/codeInsight/editorActions/FoldingData"while constructing DataFlavor for: application/x-java-jvm-local-objectref; class=com.intellij.codeInsight.editorActions.FoldingData
Exception "java.lang.ClassNotFoundException: com/intellij/codeInsight/editorActions/ReferenceData"while constructing DataFlavor for: application/x-java-jvm-local-objectref; class=com.intellij.codeInsight.editorActions.ReferenceData
Exception "java.lang.ClassNotFoundException: com/intellij/codeInsight/editorActions/ReferenceData"while constructing DataFlavor for: application/x-java-jvm-local-objectref; class=com.intellij.codeInsight.editorActions.ReferenceData

Selenium

核心,另起一篇:Java+Selenium实现网页截图

html2image

引用依赖:

<dependency><groupId>com.github.xuwei-k</groupId><artifactId>html2image</artifactId><version>0.1.0</version>
</dependency>

优缺点

html2image是可以识别html标签并将html转换成图片的java项目。
优点:后台转换,故对浏览器的版本基本没有要求。
缺点:对样式的识别不是很好,转换出来的图片比较简单,基本没有可以兼容的样式。

Cssbox

依赖:

<dependency><groupId>net.sf.cssbox</groupId><artifactId>cssbox</artifactId><version>5.0.0</version>
</dependency>

直接给代码:

public static void cssBox() throws IOException, InterruptedException, SAXException {ImageRenderer render = new ImageRenderer();render.setWindowSize(new Dimension(800, 1000), false);FileOutputStream out = new FileOutputStream("cssbox.png");Thread.sleep(10000);render.renderURL("http://www.samr.gov.cn/ggjgs/sjdt/gzdt/202001/t20200115_310511.html", out, ImageRenderer.Type.PNG);
}

优缺点

优点:无
缺点:不支持引用的外部js、css;其次,自定义设置的宽度有时候不起作用,代码内部有一个默认的宽度是2400。控制台很多报错:

10:34:46.475 [main] ERROR org.fit.cssbox.layout.ContentImage - Unable to get image from: http://www.samr.gov.cn/images/ico39.png

Robot

和Selenium比较类似。未做过多调研。

Cobra

maven搜索到一个看起来比较像能实现网页截图的依赖,并不是放在maven中央仓库的,如果可以下载jar包也行。依赖jar包下载地址为:Tidalwave,实际上已经打不开,404。另一方面,该jar包久未更新,故而未继续调研下去。

xhtmlrenderer

有依赖启动问题。

结论

Selenium牛逼。
一开始网上找到的Java版本Selenium代码也有截图不全的问题,和Python Selenium API还不是一一对应,考虑Java调用Python脚本。未免不是一种思路,但是太麻烦。

参考

Java实现网页截屏功能-PhantomJS
DjNativeSwing网页截图
使用Python+Selenium网页截图,解决截图不全问题
HTML转为图片-xhtmlrenderer
HTML转为图片-Cobra
HTML转为图片-Robot
Cssbox实现HTML转图片


http://chatgpt.dhexx.cn/article/CaS9JZq0.shtml

相关文章

2种方式!带你快速实现前端截图

导语 | 本文将介绍在前端开发中页面截图的两种方式&#xff0c;包括对其实现原理和使用方式进行详细阐述&#xff0c;希望能为更多前端开发者提供一些经验和帮助。 一、 背景 页面截图功能在前端开发中&#xff0c;特别是营销场景相关的需求中, 是比较常见的。比如截屏分享&…

Element el-row el-col 布局组件详解

点此查看全部文字教程、视频教程、源代码 本文目录 1. 背景2. 分栏布局3. 分栏间隔4. 分栏偏移4. 对齐方式5. 响应式布局6. 小结 1. 背景 element的布局方式与bootstrap原理是一样的&#xff0c;将网页划分成若干行&#xff0c;然后每行等分为若干列&#xff0c;基于这样的方式…

Android基础篇 屏幕横竖屏切换(layout-land)下篇

默认情况下Activity进行屏幕旋转会自动进行onDestroy并重新onCreate 一、非默认情况下 在AndroidManifest.xml的Activity中配置 <activityandroid:name".ui.activity.XXXXXActivity"android:launchMode"singleTask"android:screenOrientation"por…

Android屏幕共享解决方案

屏幕共享是增强互动体验&#xff0c;提高沟通效率的重要功能。以下是一些实现Android屏幕共享的解决方案&#xff1a; 1、使用视频通话功能&#xff1a;在Android中&#xff0c;可以使用视频通话功能实现屏幕共享。通过视频通话&#xff0c;可以将自己的屏幕内容以视频的方式分…

让div居中的方式的几种方法

让div水平居中的方式的几种方法。 文章目录 一、margin二、绝对定位三、子元素绝对定位父元素相对定位四、flex布局总结 一、margin 第一种方式我们可以利用外边距属性来使div水平垂直居中 先来看一段有问题的代码 <!DOCTYPE html> <html lang"en"> &…

html网页图片和文字水平居中垂直居中显示

关注公众号&#xff1a;”奇叔码技术“ 回复&#xff1a;“java面试题大全”或者“java面试题” 即可免费领取资料 下面代码一致&#xff1a;示例 自己做的网站效果示例 div相对于页面水平居中显示&#xff1a; 核心代码&#xff1a;margin&#xff1a;0 auto&#xff1b;…

Android 双屏异显(Presentation) 开发,将第二个页面投屏到副屏上

1. 背景 最近开发的一个项目&#xff0c;有两个屏幕&#xff0c;需要将第二个页面投屏到副屏上&#xff0c; 这就需要用到Android的双屏异显(Presentation)技术了&#xff0c;研究了一下&#xff0c;这里做下笔记。 我们那个副屏是一块汽车的后视镜(流媒体后视镜)&#xff0c;…

父子div元素水平垂直居中的七种方法

效果样式图如下: 方法一:利用定位和transform的方法来进行居中 说明&#xff1a;首先利用定位中的子元素绝对定位和父元素相对定位的方法来,top:50% 和left:50%会使子元素在父元素中向下移动250px,向右移动250px,子元素因自身有高度和宽度,这会导致子元素不能完全居中的情况,t…

用户体验思考与flex三坑:元素不均分、溢出不省略和垂直不滚动

flex已经越来越成为前端不可避免的话题。曾经为了搞清flex的原理偶然画了一张图。但后来发现只是冰山一角。 在某些你想实现的交互效果中使用flex后可能会发现并不起作用。通过我的实践&#xff0c;大致有三个问题&#xff1a;子元素不平分父元素空间、对文字设置了溢出省略却…

DIV居中的经典方法

参考&#xff1a;https://www.cnblogs.com/rubykakas/articles/7992662.html 1. 实现DIV水平居中 设置DIV的宽高&#xff0c;使用margin设置边距0 auto&#xff0c;CSS自动算出左右边距&#xff0c;使得DIV居中。 div{width: 100px;height: 100px;margin: 0 auto; } 缺点&…

局域网屏幕共享_给安卓手机连一个大屏幕——多端协作(六)

好久不见,时下情势严峻,我们这儿封村了,生活变得更加不方便了。本来在网上买了肉,等到终于发货送到县城,我却不能进城去取了…… 希望顺丰营业点有冰箱,我的肉不会坏掉 今天给大家介绍的是Android系统的无线投屏功能,简单来说就是将支持的设备作为安卓手机的外接屏幕,以…

安卓手机与蓝牙模块联合调试(二)—— 单片机蓝牙控制LED灯亮灭(上)

系列博文&#xff1a; &#xff08;1&#xff09;安卓手机与蓝牙模块联合调试&#xff08;一&#xff09;——蓝牙模块的串口通讯 &#xff08;2&#xff09;安卓手机与蓝牙模块联合调试&#xff08;二&#xff09;—— 单片机蓝牙控制LED灯亮灭&#xff08;上&#xff09; …

android 多屏幕显示activity,副屏,无线投屏

目录 1. 首先&#xff0c;需要一个副屏 1. 1 可以通过代码的形式自己创建VirtualDispaly ,创建副屏。 1.2 或者&#xff0c;在手机的开发者模式中直接开启模拟副屏&#xff0c;也是可以的。 2.0 怎么利用这个副屏幕&#xff1f; 2.1 用作 presentation 演示ppt&#xff1…

【Android 屏幕扩展/共享】5分钟搞定—电脑 与 手机共享屏幕

回去看看老家是不是还有旧平板电脑&#xff0c;可以跨设备&#xff0c;共享屏幕哦 一些远程控制软件&#xff0c;诸如&#xff1a;TeamViewer、AnyDesk等。 但是这次我要介绍的是 一款远程显示软件&#xff0c;甚至可以理解成一款简易的KVM系统。 虽然市面上许多远程控制软件…

Android设备之间投屏功能实现

简介 简单实现两个android设备之间的投屏功能。设备间通信是通过局域网&#xff0c;需要连接同一个wifi。 录屏用到系统的MediaProjection&#xff0c;MediaProjectionManager&#xff0c;而编解码用的是MediaCodec&#xff0c;所以设备需要有DSP芯片&#xff0c;大部分手机应…

CSS实现垂直居中的5种方法

第一种 position定位margin负距离 前提是知道居中元素的宽高&#xff0c;首先给居中元素定位&#xff0c;之后设置margin的负距离为具体宽高的一半便可达到垂直居中效果 <style>.box1 {height: 300px;width: 300px;border: 10px solid pink;position: relative;}.box2 {…

在html中如何使div在页面中居中显示

在html中如何使div在页面中居中显示 最近无聊中又再温习了下html,发现好多东西都忘了。尝试着写了一个html网页&#xff0c;结果就连div如何在页面中居中显示都查了好久才弄出来。其实我不知道为什么这样可以实现&#xff0c;因为css还没仔细研究过&#xff0c;等我参加完复试定…

实现div里的img图片水平垂直居中

body结构 <body><div><img src"1.jpg" alt"haha"></div> </body>方法一&#xff1a; 将display设置成table-cell&#xff0c;然后水平居中设置text-align为center&#xff0c;垂直居中设置vertical-align为middle。 <s…

程序运行的错误

程序的运行当可能会出现的问题 没有找到文件&#xff0c;可能是相应的文件没有编写&#xff0c;或者文件名输入的有错误 程序运行可能会出现500的错误&#xff0c;错误原因大概有&#xff0c;下面几种

小程序错误

小程序报错1&#xff1a;unknown: Unexpected token, expected “,” [ appservice 生成错误] pages/page04/page04.js: file: pages/page04/page04.js unknown: Unexpected token, expected “,” (25:4) 错误原因&#xff1a;没有逗号。 小程序报错2 设置 enable-flex 属…