首页 > 编程语言 >Java实现HTML页面截图功能

Java实现HTML页面截图功能

时间:2022-11-01 10:38:05浏览次数:70  
标签:截图 Java String height width HTML static LS new


概述

业务开发中,经常会有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,截图失败:

Java实现HTML页面截图功能_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() {
// 监听加载进度
@Override
public 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转图片


标签:截图,Java,String,height,width,HTML,static,LS,new
From: https://blog.51cto.com/u_15851118/5811957

相关文章

  • Java实验报告-计算器(AWT图形界面)
     一、实验目的掌握图形用户界面的设计与实现。二、实验内容使用图形界面制作一个计算器并实现相应功能。三、实验步骤publicclassfirstappextendsAppletimplementsAc......
  • Java实验报告--计时器(线程)
    一、实验目的了解Java线程的使用方法二、实验内容1、使用多线程制作一计时器,要求实现文本框输入一个时间(分),计时结束后提示。2、系统通过点击按钮可实现启动计时、暂停、结束......
  • Java的基本使用
    两个部分:一是理论、二是实践;这里的Java讲解来源于2010年之前,之后的改变看官网。一、Java的基本概念1Java发展历史由sun(Stanforduniversitynetwork)开发,1982年2月成立,2009年......
  • 设计模式-模板模式在Java中的使用示例
    场景模板模式模板模式又叫模板方法模式(TemplateMethodPattern),是指定义一个算法的骨架,并允许子类为一个或者多个步骤提供实现。模板模式使得子类可以在不改变算法......
  • Java解析cron表达式
    概述Cron表达式是一个字符串,以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,即两种语法格式:SecondsMinutesHoursDayofMonthMonthDayofWeekYear,即:秒分时天月星......
  • Java8学习笔记
    Java8引入函数式编程,好处:代码简洁,意图明确,使用stream接口而不是for循环。多核友好,parallel()方法。相关知识高阶函数高阶函数就是接收函数参数的函数,能够根据传入的函数参......
  • Java学习之位运算(操作)总结
    最近在反思工作第四年的深度,故而写此系列。其他Java系列文章:​​Java学习之编译、反编译以及字节码入门​​​​Java学习之String​​​​Java学习之JDK9新特性​​位操作,......
  • Java学习之NoClassDefFoundError、ClassNotFoundException、NoSuchMethodError
    在菜逼如我短短的三年职业编码生涯中,无数次遇到这两个异常,故而总结一下。Java异常体系大致提一些,不是本文的重点。两者都是标准异常,平时碰到最多的是ClassNotFoundExceptio......
  • Java学习之String
    概述写在前面,工作第四年,重新把基础抓起来吧。String可以说是JDK中最基础的一个类。就记录一些日常开发中最常用的方法。String类是非可变类,其对象一旦创建,就不可销毁。Strin......
  • Java学习之JDK9新特性
    写在前面:现在(2019-01-12)绝大多数的公司或者个人都在使用JDK8,这一点毋庸置疑,但是不排除那些需要自我反省一下的落后者还在使用JDK5~7。毕竟JDK12都出来了。参考​​​JDK12......