适配需求
- 有正确完整的地址url;
- 通过浏览器能打开该url对应页面;
- 需要后台可以自动‘截屏’该页面;
- ‘截屏’后的页面可以输出文件(如pdf、png)格式;
方案一、使用JxBrowser
简介 :JxBrowser 具有多进程架构。 它在独立的本地进程中运行 Chromium,这些进程通过进程间通信 (IPC) 通道以光速与 Java 进行通信。 如果 Chromium 中出现错误,您的 Java 进程将保持活动状态。 这一切都与良好的用户体验和用户数据安全有关。
架构逻辑如下
使用步骤:
- 先申请获得30天免费使用秘钥,也可以直接付费地址
- 获取 支持的Jar 包:
- Coding :
import static com.teamdev.jxbrowser.engine.RenderingMode.OFF_SCREEN;
import static com.teamdev.jxbrowser.print.PaperSize.ISO_A4;
import com.teamdev.jxbrowser.browser.Browser;
import com.teamdev.jxbrowser.browser.callback.PrintCallback;
import com.teamdev.jxbrowser.browser.callback.PrintHtmlCallback;
import com.teamdev.jxbrowser.browser.callback.SaveAsPdfCallback;
import com.teamdev.jxbrowser.browser.event.PrintPreviewOpened;
import com.teamdev.jxbrowser.engine.Engine;
import com.teamdev.jxbrowser.engine.EngineOptions;
import com.teamdev.jxbrowser.engine.RenderingMode;
import com.teamdev.jxbrowser.frame.Frame;
import com.teamdev.jxbrowser.print.PdfPrinter;
import com.teamdev.jxbrowser.print.PrintJob;
import com.teamdev.jxbrowser.print.event.PrintCompleted;
import java.nio.file.Path;
import java.nio.file.Paths;
public class JxBrowserDemo {
// 构造一个浏览器实例
public JxBrowserDemo() {
// 设置证书秘钥
System.setProperty("jxbrowser.license.key", "此处略");
}
// 执行方法
public void run(String url) {
EngineOptions engineOptions = EngineOptions.newBuilder(OFF_SCREEN).build();
// 初始化 Chromium 引擎
Engine engine = Engine.newInstance(engineOptions);
// 创建一个浏览器实例
Browser browser = engine.newBrowser();
// 等待加载url完成
browser.navigation().loadUrlAndWait(url);
// 打印网络页面
browser.mainFrame().ifPresent(frame -> System.out.println(frame.html()));
browser.set(PrintCallback.class, (params, tell) -> {
tell.print();
});
// 设置pdf文件导出位置
browser.set(PrintHtmlCallback.class, (params, tell) -> {
Path path = Paths.get("/Users/*****/temp3.pdf");
PdfPrinter<PdfPrinter.HtmlSettings> printer = params.printers().pdfPrinter();
PrintJob<PdfPrinter.HtmlSettings> printJob = printer.printJob();
printJob.settings()
.paperSize(ISO_A4)
.enablePrintingBackgrounds()
.pdfFilePath(path)
.apply();
printJob.on(PrintCompleted.class, event -> {
if (event.isSuccess()) {
System.out.println("Printing is completed successfully.");
} else {
System.out.println("Printing has failed.");
}
});
tell.proceed(printer);
});
browser.mainFrame().ifPresent(frame -> {
frame.print();
});
try {
Thread.sleep(100000);
}catch (InterruptedException e) {
// do nothing
}
// 关闭引擎释放资源
engine.close();
}
}
方案二、JavaFX WebView
JavaFX WebView 是在 2014 年成为 JDK 8 的一部分的 JavaFX 2.0 中引入的。
如果您使用 Java 8,那么您不需要做任何特别的事情来开始使用 JavaFX WebView。
使用JDK 11及更高版本的JavaFX不再捆绑,因此要使用JavaFX 11或更高版本进行开发,您必须单独下载。
JavaFX在您的Java进程中初始化并运行WebKit。JavaFX允许您非常快速地创建和显示WebView。
然而WebKit会分配和使用Java进程的内存和CPU,一些现代网页可能会分配超过1GB的RAM。
您创建和加载网页的WebView实例越多,Java应用程序的RAM就越多(耗内存)。
使用步骤:
不需要额外操作直接coding即可
Coding :
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
// 需要继承 javafx.application.Application
public class JavaFxDemo extends Application {
private Scene scene;
@Override
public void start(Stage stage) throws Exception {
// 创建画布
stage.setTitle("Web View");
// 设置场景
scene = new Scene(new Browser(), 750, 500, Color.web("#666970"));
stage.setScene(scene);
// 风格样式
scene.getStylesheets().add("webviewsample/BrowserToolbar.css");
stage.show();
}
}
// 需要继承 Region
class Browser extends Region {
final WebView browser = new WebView();
// 浏览器引擎
final WebEngine webEngine = browser.getEngine();
public Browser() {
// 浏览器应用风格
getStyleClass().add("browser");
// 加载web页面
webEngine.load("http://www.oracle.com/products/index.html");
// 页面增加到引擎中
getChildren().add(browser);
}
private Node createSpacer() {
Region spacer = new Region();
HBox.setHgrow(spacer, Priority.ALWAYS);
return spacer;
}
@Override protected void layoutChildren() {
double w = getWidth();
double h = getHeight();
layoutInArea(browser,0,0,w,h,0, HPos.CENTER, VPos.CENTER);
}
@Override protected double computePrefWidth(double height) {
return 750;
}
@Override protected double computePrefHeight(double width) {
return 500;
}
}
public class Application {
public static void main(String[] args) {
System.out.println("hello, world");
javaFxDemo.run("https://www.baidu.com");
}
}
方案三、Headless Chrome
简介:Headless Chrome 是 Chrome 浏览器的无界面形态,可以在不打开浏览器的前提下,使用所有 Chrome 支持的特性运行你的程序。
原理: 通过chromedriver 驱动加载对应页面,可以后台获取截图,并按照文件、字节流等方式返回;
使用步骤:
- 下载 chromedriver 和 同版本 的 chrome 到本地 驱动地址;
- 放置到系统对应位置(win系统在Windows目录下,Mac在包含bin的目录下),设置好执行权限;
- 引入 selenium-server-standalone.jar 依赖
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.6.0</version>
</dependency>
- Coding :
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
@Service
public class ScreenshotService {
private static WebDriver driver;
@PostConstruct
private void initDriver(){
ChromeOptions options = new ChromeOptions();
/*
* 这里是设置要执行的命令
* --headless: 不提供可视化页面(无头模式)
* --disable-gpu: 禁用GPU加速
* --window-size: 修改截图页面的尺寸 "--window-size=1920,1200"
* --ignore-certificate-errors:
*/
options.addArguments("--headless", "--disable-gpu", "--window-size=1920,1920", "--ignore-certificate-errors");
options.addArguments("--disable-dev-shm-usage", "--no-sandbox");
driver = new ChromeDriver(options);
}
/**
* 根据网络url获取网络截屏的字节数组
* @param url 网络url
* @return 字节数组
*/
private static synchronized byte[] getFileByteArry(String url){
// 1.打印chromedriver驱动
log.info("[页面抓取]- {}", driver);
long startTime = System.currentTimeMillis();
// 2.加载web页面
driver.get(url);
// 3.页面等待渲染时长,如果你的页面需要动态渲染数据的话一定要留出页面渲染的时间,单位默认是秒
new WebDriverWait(driver, 5);
// 4.获取到截图的文件字节
byte[] byteArry = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
log.info("[页面抓取]ChromeDriver处理结束用时{}s, Title:{}", calUsedTime(startTime), driver.getTitle());
return byteArry;
}
}
【开发过程中我实际遇到的问题】:
1.代码指定浏览器驱动位置无效, 生成驱动时按照系统自动加载:win系统在Windows目录下,Mac在包含bin的目录下;
2.Mac系统中驱动需要权限,Win不需要可权限可直接执行;
3.页面大小设置问题:不提供可视化页面情况下必须指定截图页面尺寸,自上而下自左而右截取(可能有丢失内容风险);
4.如果设置页面渲染超时间(可以去除该设置):默认需要指定加载到页面Element标签ID,如果页面不含该ID则报错;
综上方案对比
JxBrowser | JavaFX | Headless | |
---|---|---|---|
开源 | 否,需付费 | 是 | 是 |
依赖 | 第三方包 | JDK | selenium +谷歌驱动 |
操作 | 复杂,需要申请令牌和增加依赖 | 简单 | 复杂,下载安装驱动和浏览器并增加依赖 |
代码复杂度 | 简单,依赖包强大 | 稍复杂,需要有抽象和分层概念 | 简单,面向对象概念 |
耗时 | 快 2-3秒 | 一般 3-5秒 | 慢 4-6秒 |
页面效果 | nice | 一般 | nice |
不足 | 依赖第三方服务 | 无法后台存储,需要弹窗选择 | 执行稍慢 |
记录我的一个失败方案
思路:通过get请求url获得一个html响应,将html打印获取页面;
失败原因:html需要在线获取一些前端样式(js、css)渲染,如果直接打印可能只是一个空页面。必须要经过web端进行渲染才能实现基本的页面格式。
参考
文章为个人实际开发完成后的技术整理,
有相关问题可以参考我当时查资料的参考(帮您节省时间),如下:文章来源:https://www.toymoban.com/news/detail-760635.html
JxBrowser
知乎 · 什么是 JxBrowser
JavaFX入门介绍
知乎 · JxBrowser还是JavaFX WebView
CSDN · Headless Chrome
CSDN · 使用ChromeHeadLess文章来源地址https://www.toymoban.com/news/detail-760635.html
到了这里,关于JAVA服务端实现页面截屏(附代码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!