Webview avec JavaFX et Screenshot

13 juillet 2025 Code JavaWebviewJavaFXScreenshost

Pour un projet personnel, j'ai eu besoin récemment de faire des captures d'écran d'un site web en java. J'ai même envisagé d'installer un serveur avec un outil comme cypress, mais FLEMME.

Une webview avec Java FX

La solution la plus simple que j'ai trouvé est d'utiliser JavaFX.

Import

Voici l'import Maven que j'ai ajouté à mon projet.

<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-web</artifactId>
    <version>21.0.5</version>
</dependency>

La version de JavaFX à importer dépend de la version de votre JDK :

Version JDK Version JavaFX
1117
1721
2123

Exemple de code

Ensuite afficher le site n'est clairement pas le plus dur :

public class JavaFxImgCapture extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        WebView webView = new WebView();

        webView.getEngine().load("https://SomeWebSite.com");

        Scene scene = new Scene(webView, 800, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

Lancer le programme

Le plus simple que j'ai trouvé est de passer par une commande maven grâce à ce plugin :

<plugin>
     <groupId>org.openjfx</groupId>
     <artifactId>javafx-maven-plugin</artifactId>
     <version>0.0.8</version>
     <configuration>
          <mainClass>monpackage.JavaFxImgCapture</mainClass>
     </configuration>
</plugin>

Et on peu l'exécuter avec la commande suivante :

mvn javafx:run

Faire une Capture d'écran

Cela à été beaucoup plus compliquer qu'il n'y parait. Il faut :

  • attendre que le site soit chargé
  • attendre que les ressources soit chargées (image, javascript par exemple)
  • faire la capture

La difficulté étant qu'il ne faut pas faire un sleep dans un thread JavaFX et qu'on ne peu pas exécuter un commande JavaFX dans un thread qui n'est pas d'origine de JavaFX.

Donc si je reprend la liste précédente il faut faire comme cela :

  • attendre que le site soit chargé : Callback JavaFX.
  • attendre que les ressources soit chargées (image javascript) : Faire un pause dans un thread classique.
  • faire la captures : Prendre la capture dans un Thread JavaFX.

Import

On va avoir besoin d'import supplémentaire :

<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-swing</artifactId>
    <version>21.0.5</version>
</dependency>

Code Java

public class JavaFxImgCapture extends Application implements ChangeListener<Worker.State> {

    public static void main(String[] args) {
        launch(args);
    }

    private WebView webView

    @Override
    public void start(Stage primaryStage) throws Exception {
        webView = new WebView();
        webView.getEngine().load("https://SomeWebSite.com");
        
        // enregistrement du callback :
        webView.getEngine().getLoadWorker().stateProperty().addListener(this);

        Scene scene = new Scene(webView, 800, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    @Override
    public void changed(ObservableValue<? extends State> observable, State oldState, State newState) {
        // Le site est chargé
        if (newState == Worker.State.SUCCEEDED) {
            waitInStandartThread();
        }
    }
    
    private void waitInStandartThread() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(SLEEP_TIME);
                    doCaptureInJavaFxThread();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
    
    private void doCaptureInJavaFxThread() {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                doCapture();
            }
        });
    }

    private void doCapture() {
        WritableImage snapshot = webView.snapshot(null, null);
        BufferedImage bufferedImage = SwingFXUtils.fromFXImage(snapshot, null);
        try {
            ImageIO.write(bufferedImage, "png", new File("SomeOutputFile.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

Sans interface graphique

Si comme moi vous avez votre serveur linux qui automatise des choses, vous n'avez probablement pas d'interface graphique sur celui-ci et donc vous ne pouvez pas lancer un programme en JavaFX.

La solution que j'ai trouvé est d'utiliser le serveur X FrameBuffer. Ce serveur X émule la sortie écran en mémoire, d'où le nom framebuffer.

sudo apt install xvfb

Et pour lancer un programme dans ce serveur X il suffit de préfixer la commande par xvfb-run. Par exemple :

xvfb-run mvn javafx:run
par Shionn, le 13 juillet 2025.

Commentaires

Ajouter un commentaire

Calendrier