JAVA服务端实现页面截屏(附代码)

适配需求

  1. 有正确完整的地址url;
  2. 通过浏览器能打开该url对应页面;
  3. 需要后台可以自动‘截屏’该页面;
  4. ‘截屏’后的页面可以输出文件(如pdf、png)格式;

方案一、使用JxBrowser

简介 :JxBrowser 具有多进程架构。 它在独立的本地进程中运行 Chromium,这些进程通过进程间通信 (IPC) 通道以光速与 Java 进行通信。 如果 Chromium 中出现错误,您的 Java 进程将保持活动状态。 这一切都与良好的用户体验和用户数据安全有关。​

架构逻辑如下
在这里插入图片描述

使用步骤:

  1. 先申请获得30天免费使用秘钥,也可以直接付费地址
  2. 获取 支持的Jar 包:
  3. 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 驱动加载对应页面,可以后台获取截图,并按照文件、字节流等方式返回;

使用步骤:

  1. 下载 chromedriver 和 同版本 的 chrome 到本地 驱动地址
  2. 放置到系统对应位置(win系统在Windows目录下,Mac在包含bin的目录下),设置好执行权限;
  3. 引入 selenium-server-standalone.jar 依赖
<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>3.6.0</version>
</dependency>
  1. 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则报错;


综上方案对比

JxBrowserJavaFXHeadless
开源否,需付费
依赖第三方包JDKselenium +谷歌驱动
操作复杂,需要申请令牌和增加依赖简单复杂,下载安装驱动和浏览器并增加依赖
代码复杂度简单,依赖包强大稍复杂,需要有抽象和分层概念简单,面向对象概念
耗时快 2-3秒一般 3-5秒慢 4-6秒
页面效果nice一般nice
不足依赖第三方服务无法后台存储,需要弹窗选择执行稍慢

记录我的一个失败方案

思路:通过get请求url获得一个html响应,将html打印获取页面;
失败原因:html需要在线获取一些前端样式(js、css)渲染,如果直接打印可能只是一个空页面。必须要经过web端进行渲染才能实现基本的页面格式。


参考

文章为个人实际开发完成后的技术整理
有相关问题可以参考我当时查资料的参考(帮您节省时间),如下:

JxBrowser
知乎 · 什么是 JxBrowser
JavaFX入门介绍
知乎 · JxBrowser还是JavaFX WebView
CSDN · Headless Chrome
CSDN · 使用ChromeHeadLess

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值