整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

如何在 Java 中将 HTML 转换为 PNG

日分享最新,最流行的软件开发知识与最新行业趋势,希望大家能够一键三连,多多支持,跪求关注,点赞,留言。

本文讨论了对 HTML 代码可视化文档的需求,并提供了一个免费的 API 解决方案,可将 HTML 字符串转换为 PNG 屏幕截图。

自 20 世纪 80 年代后期的构想以来,超文本标记语言 (HTML) 一直是在线显示网页的关键元素。这种无处不在的编程语言继续提供详细的框架,用于构建我们在 Web 上看到和交互的内容,允许我们以纯文本代码格式化文本和多媒体组件,这很简单,可以在需要时进行更改。
HTML 的转变
与几乎所有编程语言的情况一样,HTML自推出以来的几十年中已经转变为包含许多新功能,以适应典型的当代压力,例如社区反馈/批评和相邻 Web 开发技术的快速发展。在现代 HTML 代码的输出中,我们可以很容易地看到这种转换的结果;例如,最近的 HTML 迭代——HTML5,于 2014 年推出——提供了用于嵌入视频和音频文件的新的、简单的元素,以及移动显示和整体移动功能中急需的改进。

当然,新元素和显示质量的改进并不是网站的 HTML 代码会随着时间的推移可靠地改变的唯一原因。网站不断设计和重新设计,可能是为了创新,也是为了响应用户反馈产生的趋势。例如,2014 年开发的网站很可能融合了各种当代设计趋势,而与当年 HTML5 中引入的变化无关。在该项目之后的几年里,该网站的开发人员可能会发现自己至少多次重复了他们的 HTML 代码的一部分,同时一直在为不可避免的 HTML6 发布和最终合并其新的和改进的功能而构建。

鉴于 HTML 开发中的这些自然进展,出现了一个重要问题:我们如何有效地跟踪和记录我们网站 HTML 代码的增长?考虑到 HTML 输出的固有视觉特性,答案相对简单。我们可以轻松地以静态二维图像文件(屏幕截图)的形式存储 HTML 网站的迭代,并且我们可以相对轻松地以编程方式完成此转换。

网页截图
捕获 HTML 屏幕截图具有大量实际的业务应用程序。当为网站编写新的 HTML 代码时,渲染该 HTML 代码输出的图像作为一种简单、易于共享的“状态检查”,用于检查其内容在给定时间点如何显示在 Web 浏览器上。出于同样的原因,这样的屏幕截图提供了一种极好的方法来快速测试 HTML 代码的新的、实验性的迭代,使开发人员可以轻松地创建和存储开发中项目的各种版本——包括成功的和不成功的。屏幕截图还为实时网站中不可避免的问题提供了一种理想的可视化记录方式,从而更容易跟踪棘手的问题并跟踪它们在不同设备、浏览器或操作系统上的显示方式。

教程
本教程的目的是提供一个简单、免费、易于使用的 API 解决方案,用于在 Java 中将 HTML 字符串转换为 PNG 屏幕截图。此 API 将完整呈现网站,返回 HTML 在常规 Web 浏览器视图中显示内容的屏幕截图。它支持所有现代、高级的 Web 开发功能,包括与 HTML5、CSS、JavaScript等相关的功能。为方便起见,页面下方提供了现成的 Java 代码示例,以帮助您轻松构建 API 调用。

该接口有两个必填的请求参数,包括以下内容:

你的 HTML 字符串
一个免费的 Cloudmersive API 密钥(您可以通过访问我们的网站并注册一个免费帐户来获得一个)。
除了上述强制输入外,此 API 还提供了几个可选参数,允许进一步自定义您的输入请求。这些可选参数包括以下内容:

Extra loading wait:网页完成加载后截屏前等待的额外毫秒数(对于非常异步的网站很有帮助)。
屏幕截图高度:屏幕截图的所需高度,以像素表示(默认为 1280 x 1024)。提供整数“0”会触发默认设置,而提供整数“-1”会要求 API 测量并尝试屏幕高度屏幕截图。
屏幕截图宽度:屏幕截图的所需宽度,以像素表示(也默认为标准 1280 x 1024 测量值)。提供整数“0”或“-1”会产生与上述“屏幕截图高度”参数中所述相同的结果。
在其响应中,此 API 将提供一个包含新 PNG 文件编码的字符串。

要在Java中构建 API 调用,第一步是安装 SDK。这可以使用 Maven 来完成,方法是首先将以下引用添加到存储库中pom.xml:

<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>



之后,将下面的引用添加到依赖项中pom.xml:

<dependencies>
<dependency>
<groupId>com.github.Cloudmersive</groupId>
<artifactId>Cloudmersive.APIClient.Java</artifactId>
<version>v4.25</version>
</dependency>
</dependencies>



要改为使用 Gradle 安装SDK,请在根目录build.gradle(存储库末尾)中添加您的引用:

allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}


然后添加依赖项build.gradle:

dependencies {
implementation 'com.github.Cloudmersive:Cloudmersive.APIClient.Java:v4.25'
}


安装完成后,剩下的就是复制并粘贴以下代码示例,并完成如上所述的强制和可选请求参数:

// Import classes:
//import com.cloudmersive.client.invoker.ApiClient;
//import com.cloudmersive.client.invoker.ApiException;
//import com.cloudmersive.client.invoker.Configuration;
//import com.cloudmersive.client.invoker.auth.*;
//import com.cloudmersive.client.ConvertWebApi;

ApiClient defaultClient = Configuration.getDefaultApiClient();

// Configure API key authorization: Apikey
ApiKeyAuth Apikey = (ApiKeyAuth) defaultClient.getAuthentication("Apikey");
Apikey.setApiKey("YOUR API KEY");
// Uncomment the following line to set a prefix for the API key, e.g. "Token" (defaults to null)
//Apikey.setApiKeyPrefix("Token");

ConvertWebApi apiInstance = new ConvertWebApi();
HtmlToPngRequest input = new HtmlToPngRequest(); // HtmlToPngRequest | HTML to PNG request parameters
try {
byte[] result = apiInstance.convertWebHtmlToPng(input);
System.out.println(result);
} catch (ApiException e) {
System.err.println("Exception when calling ConvertWebApi#convertWebHtmlToPng");
e.printStackTrace();
}


一旦你完成了这一步,你就大功告成了——你现在可以调用这个 API 并轻松地将 HTML 字符串呈现为 PNG 屏幕截图。

注意: 您的免费层级 API 密钥将提供每月 800 次 API 调用的限制,无需承诺。一旦达到该限制,您的总数将在下个月重置。

需求一直有,今年比较多,如题,工作中遇到网页截图这样的需求,本着效果好,功能全又稳定的意图,去网上搜索相关技术,像HTML2Image、cssbox、selenium等,还有很多其他的技术,这篇文章主要说说我测试使用并能满足需求的cssbox,selenium。

Cssbox

CSSBox是一个用纯Java编写的(X)HTML/CSS渲染引擎。它的主要目的是提供关于呈现的页面内容和布局的完整和进一步可处理的信息。 但是,它也可以用于浏览Java Swing应用程序中呈现的文档。核心CSSBox库还可以用于获得所呈现的文档的位图或矢量(SVG)图像。 使用SwingBox包,CSSBox可以用作Java Swing应用程序中的交互式Web浏览器组件。

官网地址:http://cssbox.sourceforge.net/

使用

1引入maven依赖

<!--网站转换为图片cssbox-->
<dependency>
<groupId>net.sf.cssbox</groupId>
<artifactId>cssbox</artifactId>
<version>5.0.0</version>
</dependency>

2使用

@Test
public void cssboxTest(){
    try {
        ImageRenderer render = new ImageRenderer();
        //网络链接的html
        String url = "https://www.zhangbj.com/p/524.html";
        //文件保存路径
        String path = "C:\\Users\\Administrator\\Desktop"+File.separator+"html.png";
        FileOutputStream out = new FileOutputStream(new File(FilenameUtils.normalize(path)));
        //开始截屏
        render.renderURL(url, out);
    } catch (Exception e) {
    e.printStackTrace();
    }
}

3结果

样式可能出现问题,中文有时候乱码

Selenium

1引入依赖

<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.141.59</version>
</dependency>

2相关准备

selenium+chromedriver谷歌驱动+chrome浏览器

1.注意谷歌驱动的版本要和谷歌浏览器的版本一样或者版本最相近

2.注意chromedriver谷歌驱动需要放在jdk安装目录下,具体路径为xxx/bin/chromedriver.exe,在linux和window中操作一样,这样切换系统是就无需改代码。

3.需要安装谷歌浏览器

谷歌驱动下载地址:https://registry.npmmirror.com/binary.html?path=chromedriver/

3使用

@Slf4j
public class Html2ImageUtil {
/**
* 将HTML转为图片,并保存至指定位置
* @param url 页面地址
* @param targetPath 保存地址(包含图片名,如 /images/test.png)
* @return
*/
public static String htmlToImage(String url, String targetPath) {
  if (StringUtils.isEmpty(url) || StringUtils.isEmpty(targetPath)) {
  throw new RuntimeException("截图失败!缺少必填项");
  }
  // 休眠时长
  Integer sleepTime = 3 * 1000;
  // 无头模式
  System.setProperty("java.awt.headless", "true");
  //获取谷歌配置信息
  ChromeOptions chromeOptions = getChromeOptions();
  // 配置信息中有默认窗口大小,也可以单独设置窗口大小
  chromeOptions.addArguments("--window-size=1920,6000");
  //创建webdriver 谷歌驱动
  WebDriver driver = new ChromeDriver(chromeOptions);
  //也可以通过如下方式设置窗口大小
  // Dimension dimension = new Dimension(1000, 30);
  // driver.manage().window().setSize(dimension);
  try {
    //加载页面
    driver.get(url);
    //等待加载页面
    Thread.sleep(sleepTime);
    //截屏
    File srcFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
    //保存到指定位置
    FileUtils.copyFile(srcFile, new File(FilenameUtils.normalize(targetPath)));
  } catch (InterruptedException | IOException e) {
  e.printStackTrace();
  throw new RuntimeException(e.getMessage());
  } finally {
  driver.quit();
  }
  log.info("截图成功!");
  return targetPath;
}
/**
* 获取chrome配置信息
* 注意 chromedriver谷歌驱动需要放在jdk安装目录下,具体路径为xxx/bin/chromedriver.exe ,在linux和window中操作一样
* @return
*/
public static ChromeOptions getChromeOptions() {
    ChromeOptions options = new ChromeOptions();
    //获取当前操作系统
    String os = System.getProperty("os.name");
    //获取jdk安装目录,需要提前将谷歌驱动放进jdk的bin目录下,在linux和window中操作一样
    String sysPath = System.getProperty("java.home").replace("jre", "bin");
    String chromeDriver = sysPath + File.separator+"chromedriver.exe";
    options.addArguments("disable-infobars");
    //设置为 headless 模式,不需要真实启动浏览器
    options.setHeadless(true);
    //options.addArguments("--headless");
    options.addArguments("--dns-prefetch-disable");
    options.addArguments("--no-referrers");
    options.addArguments("--disable-gpu");
    options.addArguments("--disable-audio");
    options.addArguments("--no-sandbox");
    options.addArguments("--ignore-certificate-errors");
    options.addArguments("--allow-insecure-localhost");
    options.addArguments("--window-size=1920,6000"); // 窗口默认大小
    String userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36";
    userAgent = "user-agent=" + userAgent;
    options.addArguments(userAgent);
    // 设置chrome二进制文件
    options.setPageLoadStrategy(PageLoadStrategy.EAGER);
    // 设置驱动
    System.setProperty("webdriver.chrome.driver", chromeDriver);
    log.debug("结束获取chrome配置信息");
    return options;
}

测试

public static void main(String[] args) {
		htmlToImage("https://www.cnblogs.com/tester-ggf/p/12602211.html","C:\\Users\\Administrator\\Desktop\\aaa.png");
}

效果十分完美

总结

最完美的方案就是selenium+chromedriver谷歌驱动+chrome浏览器,无需多说,用吧。

您的赞和关注是对我创作的最大肯定谢谢大家!

OM to Image

dom-to-image是一个js库,可以将任意dom节点转换为矢量(SVG)或光栅(PNG或JPEG)图像。

安装

npm install dom-to-image -S

加载

/* in ES 6 */
import domtoimage from 'dom-to-image';
/* in ES 5 */
var domtoimage = require('dom-to-image');

用法

所有高阶函数都接受DOM节点和渲染选项options ,并返回promises。

  1. 获取PNG图像base64编码的data URL:
<div id="my-node"></div>
var node = document.getElementById('my-node');
// options 可不传
var options = {}  
domtoimage.toPng(node, options)
    .then(function (dataUrl) {
        var img = new Image();
        img.src = dataUrl;
        document.body.appendChild(img);
    })
    .catch(function (error) {
        console.error('oops, something went wrong!', error);
    });
  1. 获取图像blob:
domtoimage.toBlob(document.getElementById('my-node'))
    .then(function (blob) { 
        console.log('blob', blob)
    });
  1. 获取JPEG图像base64编码的data URL并下载:
domtoimage.toJpeg(document.getElementById('my-node'), { quality: 0.95 })
    .then(function (dataUrl) {
        var link = document.createElement('a');
        link.download = 'my-image-name.jpeg';
        link.href = dataUrl;
        link.click();
});
  1. 获取SVGdata URL,但筛选出所有元素:
function filter (node) {
    return (node.tagName !== 'i');
}
 
domtoimage.toSvg(document.getElementById('my-node'), {filter: filter})
    .then(function (dataUrl) {
        /* do something */
});
  1. 以uint8数组的形式获取原始像素数据,每4个数组元素表示一个像素的RGBA数据:
var node = document.getElementById('my-node');
 
domtoimage.toPixelData(node)
    .then(function (pixels) {
        for (var y = 0; y < node.scrollHeight; ++y) {
          for (var x = 0; x < node.scrollWidth; ++x) {
            pixelAtXYOffset = (4 * y * node.scrollHeight) + (4 * x);
            /* pixelAtXY is a Uint8Array[4] containing RGBA values of the pixel at (x, y) in the range 0..255 */
            pixelAtXY = pixels.slice(pixelAtXYOffset, pixelAtXYOffset + 4);
          }
        }
    });

options参数

Name

类型

Default

Description

filter

Function

——

以DOM节点为参数的函数。如果传递的节点应包含在输出中,则应返回true(排除节点意味着也排除其子节点)

bgcolor

String

——

背景色的字符串值,任何有效的CSS颜色值。

height

Number

——

渲染前应用于节点的高度(以像素为单位)。

width

Number

——

渲染前应用于节点的宽度(以像素为单位)。

style

Object

——

object对象,其属性在渲染之前要复制到节点的样式中。

quality

Number

1.0

介于0和1之间的数字,表示JPEG图像的图像质量(例如0.92=>92%)。默认值为1.0(100%)

cacheBust

Boolean

false

设置为true可将当前时间作为查询字符串附加到URL请求以启用清除缓存。

imagePlaceholder

Boolean

undefined

获取图片失败时使用图片的数据URL作为占位符。默认为未定义,并将在失败的图像上引发错误。

原理

dom-to-image使用SVG的一个特性,它允许在标记中包含任意HTML内容。

  • 递归地克隆原始DOM节点
  • 计算节点和每个子节点的样式,并将其复制到相应的克隆 创建伪元素,因为它们不是以任何方式克隆的
  • 嵌入web字体 查找所有@font face声明的web字体 解析文件URL,下载相应文件 base64编码的内联作为data:URLs 将所有已处理的CSS放入中,然后将其附加到克隆
  • 嵌入图片 在嵌入图片URL 使用backgroundCSS属性的图片,方法类似于字体
  • 将克隆的节点序列化为XML
  • 将XML包装到标记中,然后包装到SVG中,然后使其成为data URL
  • 或者,要以Uint8Array的形式获取PNG内容或原始像素数据,可以创建一个以SVG为源的图像元素,并将其呈现在已经创建的canvas上,从canvas读取内容

部分源码分析

dom-to-image.js

// Default impl options
var defaultOptions = {
    // Default is to fail on error, no placeholder
    imagePlaceholder: undefined,
    // Default cache bust is false, it will use the cache
    cacheBust: false
};

var domtoimage = {
    toSvg: toSvg,
    toPng: toPng,
    toJpeg: toJpeg,
    toBlob: toBlob,
    toPixelData: toPixelData,
    impl: {
        fontFaces: fontFaces,
        images: images,
        util: util,
        inliner: inliner,
        options: {}
    }
};

if (typeof module !== 'undefined')
    module.exports = domtoimage;
else
    global.domtoimage = domtoimage;
  • defaultOptions设置默认options选项
  • domtoimage的核心api:
    • toSvg
    • toPng
    • toJpeg
    • toBlob
    • toPixelData
  • 例:toJpeg:将draw函数返回的canvas实例,使用canvas的toDataURL方法生成jpeg图片。toSvg函数将递归地克隆原始DOM节点, 将克隆的节点序列化为XML,将XML包装到标记中,然后包装到SVG中,然后使其转成dataURL。
function toJpeg(node, options) {
   options = options || {};
   return draw(node, options)
       .then(function (canvas) {
           return canvas.toDataURL('image/jpeg', options.quality || 1.0);
       });
}
复制代码
function draw(domNode, options) {
    return toSvg(domNode, options)
        .then(util.makeImage)
        .then(util.delay(100))
        .then(function (image) {
            var canvas = newCanvas(domNode);
            canvas.getContext('2d').drawImage(image, 0, 0);
            return canvas;
        });

    function newCanvas(domNode) {
        var canvas = document.createElement('canvas');
        canvas.width = options.width || util.width(domNode);
        canvas.height = options.height || util.height(domNode);

        if (options.bgcolor) {
            var ctx = canvas.getContext('2d');
            ctx.fillStyle = options.bgcolor;
            ctx.fillRect(0, 0, canvas.width, canvas.height);
        }

        return canvas;
    }
}
function toSvg(node, options) {
    options = options || {};
    copyOptions(options);
    return Promise.resolve(node)
        .then(function (node) {
            return cloneNode(node, options.filter, true);
        })
        .then(embedFonts)
        .then(inlineImages)
        .then(applyOptions)
        .then(function (clone) {
            return makeSvgDataUri(clone,
                options.width || util.width(node),
                options.height || util.height(node)
            );
        });

    function applyOptions(clone) {
        if (options.bgcolor) clone.style.backgroundColor = options.bgcolor;

        if (options.width) clone.style.width = options.width + 'px';
        if (options.height) clone.style.height = options.height + 'px';

        if (options.style)
            Object.keys(options.style).forEach(function (property) {
                clone.style[property] = options.style[property];
            });

        return clone;
    }
}


作者:知其
https://juejin.cn/post/6988045156473634852