用于服务器端图像生成的 JavaFX [英] JavaFX for server-side image generation

查看:26
本文介绍了用于服务器端图像生成的 JavaFX的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这听起来很奇怪,但我想使用 JavaFX 在服务器端生成我的图表图像.因为 JavaFX 有很好的画布 API 来执行图像转换连接和定位.

This could sound strange but I want to generate my chart images on server side using JavaFX. Because JavaFX has nice canvas API to perform image transformations joins and positioning.

特别是我有一个 spring MVC 服务来生成我的图表作为图像.主要问题是如何从方便的 Spring bean 调用 javaFX API.如果我尝试从 java 应用程序运行 javafx 代码(不扩展 javaFX 应用程序类),我得到

In particular I have a spring MVC service to generate my charts as images. The main problem is how to invoke javaFX API from a convenient Spring bean. If I try to just run javafx code from java application (not extending javaFX Application class) I get

java.lang.IllegalStateException: Toolkit not initialized

您对如何解决此问题有任何建议/想法吗?

Do you have any suggestions/ideas how to solve this issue?

推荐答案

所以经过一些研究,我已经用 JavaFX 实现了画布绘制,这里是一个简化的例子:

So after some research I've implemented canvas draw with JavaFX and here is a simplified example:

首先,我制作了在单独线程中启动的 JavaFX 应用程序(我使用 Spring taskExecutor,但可以使用普通的 Java 线程).

First I made the JavaFX application which is being launched in a separate thread (I use Spring taskExecutor but a plain java thread can be used).

public class ChartGenerator extends Application {

    private static Canvas canvas;

    private static volatile byte[] result;

    public static void initialize(TaskExecutor taskExecutor) {
        taskExecutor.execute(new Runnable() {
            @Override
            public void run() {
                launch(ChartGenerator.class);
            }
        });
    }

    public static synchronized byte[] generateChart(final Object... params) {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                ByteArrayOutputStream baos = null;
                try {
                    GraphicsContext gc = canvas.getGraphicsContext2D();
                    gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
                    /**
                     * Do the work with canvas
                     **/
                    final SnapshotParameters snapshotParameters = new SnapshotParameters();
                    snapshotParameters.setFill(Color.TRANSPARENT);
                    WritableImage image = canvas.snapshot(snapshotParameters, null);
                    BufferedImage bImage = SwingFXUtils.fromFXImage(image, null);
                    baos = new ByteArrayOutputStream();
                    ImageIO.write(bImage, chartType.outputFormat, baos);
                    result = baos.toByteArray();
                } catch (InstantiationException e) {
                    throw new ChartGenerationException(e);
                } catch (IllegalAccessException e) {
                    throw new ChartGenerationException(e);
                } catch (NoSuchMethodException e) {
                    throw new ChartGenerationException(e);
                } catch (InvocationTargetException e) {
                    throw new ChartGenerationException(e);
                } catch (IOException e) {
                    throw new ChartGenerationException(e);
                } finally {
                    IOUtils.closeQuietly(baos);
                }
            }
        });
        while (result == null) {
            //wait
        }
        byte[] ret = result;
        result = null;
        return ret;
    }


    @Override
    public void start(Stage stage) {
        canvas = new Canvas();
    }

    public static class ChartGenerationException extends RuntimeException {
        public ChartGenerationException(String message) {
            super(message);
        }
        public ChartGenerationException(Throwable cause) {
            super(cause);
        }
    }

}

然后我在 Spring 应用程序启动时调用 initialize() 方法:

Then I call the initialize() method when the Spring application is started:

@Autowired private TaskExecutor taskExecutor;

@PostConstruct private void initChartGenerator() {
    ChartGenerator.initialize(taskExecutor);
}

这个解决方案当然可以移植到非 Spring 应用程序中.

This solution of cource can be ported to a non-Spring application.

这是一个单线程解决方案(在我的情况下已经足够了)但我认为它可以用于多线程使用(也许使用 RMI 来调用 draw 方法).

This is a single-threaded solution (in my case it's enough) but I think it could be adopted to multithreaded usage (maybe use RMI to invoke draw method).

此外,此解决方案在我的 Windows 工作站上按原样"工作,但在 linux 服务器环境中应调用一些附加操作:

Also this solution works "as is" on my windows workstation but on linux server environment some additional actions should be invoked:

  1. 您不能在 OpenJDK 上使用 JavaFX(截至 2013 年 8 月) - 必须切换到 Oracle JDK
  2. Java 版本不得低于 Java 7u6
  3. 最复杂的 - 您必须使用虚拟显示才能让 JavaFX 在无头环境中运行:

  1. You cannot use JavaFX on OpenJDK (as of Aug 2013) - have to switch to Oracle JDK
  2. Java version must be no less than Java 7u6
  3. The most complex - you have to use virtual display to make JavaFX run on headless environments:

apt-get install xvfb

apt-get install xvfb

//然后在应用服务器启动时:

// then on application server start:

导出显示=":99"

start-stop-daemon --start --background --user jetty --exec "/usr/bin/sudo" -- -u jetty/usr/bin/Xvfb :99 -screen 0 1024x768x24

start-stop-daemon --start --background --user jetty --exec "/usr/bin/sudo" -- -u jetty /usr/bin/Xvfb :99 -screen 0 1024x768x24

<小时>

附言您还可以使用此解决方案在服务器端使用其他 JavaFX 功能(例如,将 html 导出为图像).


P.S. You can also use other JavaFX capabilities on server side (e.g. export html to image) with this solution.

这篇关于用于服务器端图像生成的 JavaFX的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆