目录
问题内容
解决方法
版本
源代码
gradle 文件
执行
首页 Java 加载 JavaFX 图像并将其保存到数据库

加载 JavaFX 图像并将其保存到数据库

Feb 22, 2024 pm 02:49 PM
overflow

JavaFX是Java编程语言的一个图形用户界面工具包,它可以轻松创建丰富、现代化的用户界面。在实际开发中,有时候需要加载JavaFX图像并将其保存到数据库中。本文将为您介绍如何实现这一操作,通过简单易懂的步骤,帮助您解决这一问题。让我们一起来探讨吧!

问题内容

我现在正在努力将一个小图像保存在数据库中并将其加载以用作 javafx.scene.image.Image。我尝试过的几个解决方案涉及使用 SwingFXUtils.fromFXImage 和 SwingFXUtils.toFxImage,但该类似乎需要 module-info.java 文件中的一个条目,而我什至不打算使用该条目。

我在尝试使用 SwingFXUtils 时遇到此异常:java.lang.NoClassDefFoundError: javafx/embed/swing/SwingFXUtils

当我使用模块系统时,它破坏了我正在使用的另一个库(itextpdf)。 所以我想避免这种情况,只是找到另一种方法来保存和加载数据库中的 JavaFX 图像。 有什么建议吗?

解决方法

您提到:

这不是真的。尽管 javafx 仅支持作为命名模块加载1,但 javafx 并不要求您自己的代码是模块化的。获得 noclassdeffounderror 的唯一方法是在运行时未能将类包含在模块路径/类路径中(它必须在编译时存在,否则您的代码将无法编译)。我建议阅读 Getting Started with JavaFX,了解如何使用主要 java ide 和/或构建工具之一设置基本 javafx 应用程序。但是,如果不知道您如何配置项目以及如何运行项目,我们将无法告诉您您的设置具体出了什么问题。

也就是说,您想要完成的总体目标确实是可能的。下面是一个示例,允许您浏览计算机上的图像并将其保存到内存中的 h2 数据库中。图像的 id 和名称被放入 tableview 中,其中包含一个带有“打开”按钮的列,可让您查看从内存数据库加载的图像。图像以 png 格式作为 blob 存储在数据库中,无论其原始格式如何。 swingfxutilsimageio 类用于将 javafx 图像转换为 png“文件”2

该示例未向您展示如何部署应用程序(例如,通过 jpackage)。

版本

以下是我用于构建和运行示例的库和工具的版本。

  • java 21.0.1(eclipse adoptium / temurin)

  • javafx 21.0.1

  • h2 2.2.224

  • gradle 8.4

  • JavaFX Gradle Plugin 0.1.0

commentprevious question2 中,您声明您正在使用 gradle,因此我在示例中使用的是 gradle。

源代码

没有 module-info.java 文件。

imagerecord.java

package com.example;

public record imagerecord(int id, string name) {}
登录后复制

main.java

package com.example;

import java.io.file;
import java.util.concurrent.executor;
import java.util.concurrent.executors;
import java.util.function.consumer;
import javafx.application.application;
import javafx.beans.property.simpleintegerproperty;
import javafx.beans.property.simpleobjectproperty;
import javafx.beans.property.simplestringproperty;
import javafx.concurrent.task;
import javafx.geometry.insets;
import javafx.geometry.pos;
import javafx.scene.scene;
import javafx.scene.control.button;
import javafx.scene.control.scrollpane;
import javafx.scene.control.tablecell;
import javafx.scene.control.tablecolumn;
import javafx.scene.control.tableview;
import javafx.scene.image.image;
import javafx.scene.image.imageview;
import javafx.scene.layout.borderpane;
import javafx.scene.layout.hbox;
import javafx.stage.filechooser;
import javafx.stage.stage;
import javafx.stage.stagestyle;
import javafx.stage.window;

public class main extends application {

  private final executor executor = executors.newvirtualthreadpertaskexecutor();
  private final imagesdatabase db = new imagesdatabase("test");
  private final imagerepository imagerepo = new imagerepository(db);
  private file lastdirectory;

  @override
  public void start(stage primarystage) {
    var table = createtable(record -> displayimage(primarystage, record));

    var choosebtn = new button("choose image...");
    choosebtn.setonaction(
        e -> {
          e.consume();
          var image = chooseimage(primarystage);
          if (image != null) {
            executor.execute(createsaveimagetask(image, table.getitems()::add));
          }
        });

    var root = new borderpane();
    root.settop(choosebtn);
    root.setcenter(table);
    borderpane.setalignment(choosebtn, pos.center);
    borderpane.setmargin(choosebtn, new insets(10));

    primarystage.setscene(new scene(root, 600, 400));
    primarystage.show();
  }

  @override
  public void stop() throws exception {
    db.close();
  }

  private image chooseimage(window owner) {
    var chooser = new filechooser();
    chooser.settitle("choose image file");
    chooser
        .getextensionfilters()
        .add(new filechooser.extensionfilter("image files", "*.jpeg", "*.jpg", "*.png"));
    if (lastdirectory != null) {
      chooser.setinitialdirectory(lastdirectory);
    }

    var file = chooser.showopendialog(owner);
    if (file != null) {
      lastdirectory = file.getparentfile();
      return new image(file.touri().tostring());
    }
    return null;
  }

  private void displayimage(window owner, imagerecord record) {
    var view = new imageview();

    var task = creategetimagetask(record, view::setimage);
    executor.execute(task);

    var sp = new scrollpane(view);
    sp.setpannable(true);

    var window = new stage(stagestyle.utility);
    window.initowner(owner);
    window.settitle(record.name());
    window.setscene(new scene(sp, 500, 300));
    window.setonhiding(e -> task.cancel());
    window.show();
  }

  private tableview<imagerecord> createtable(consumer<imagerecord> onopen) {
    var table = new tableview<imagerecord>();
    table.setcolumnresizepolicy(tableview.constrained_resize_policy_flex_last_column);

    var idcol = new tablecolumn<imagerecord, number>("id");
    idcol.setcellvaluefactory(data -> new simpleintegerproperty(data.getvalue().id()));
    table.getcolumns().add(idcol);

    var namecol = new tablecolumn<imagerecord, string>("name");
    namecol.setcellvaluefactory(data -> new simplestringproperty(data.getvalue().name()));
    table.getcolumns().add(namecol);

    var openbtncol = new tablecolumn<imagerecord, imagerecord>();
    openbtncol.setcellvaluefactory(data -> new simpleobjectproperty<>(data.getvalue()));
    openbtncol.setcellfactory(tc -> createopenbuttoncell(onopen));
    table.getcolumns().add(openbtncol);

    return table;
  }

  private tablecell<imagerecord, imagerecord> createopenbuttoncell(consumer<imagerecord> onopen) {
    return new tablecell<>() {
      final hbox container = new hbox();
      final button openbutton = new button("open");

      {
        container.getchildren().add(openbutton);
        container.setalignment(pos.center);
        openbutton.setonaction(
            e -> {
              e.consume();
              var item = isempty() ? null : getitem();
              if (item != null) {
                onopen.accept(item);
              }
            });
      }

      @override
      protected void updateitem(imagerecord item, boolean empty) {
        super.updateitem(item, empty);
        if (empty || item == null) {
          setgraphic(null);
        } else {
          setgraphic(container);
        }
      }
    };
  }

  private task<?> createsaveimagetask(image image, consumer<imagerecord> onsuccess) {
    return new task<imagerecord>() {
      @override
      protected imagerecord call() throws exception {
        return imagerepo.insertimage(image);
      }

      @override
      protected void succeeded() {
        onsuccess.accept(getvalue());
      }

      @override
      protected void failed() {
        getexception().printstacktrace();
      }
    };
  }

  private task<?> creategetimagetask(imagerecord record, consumer<image> onsuccess) {
    return new task<image>() {
      @override
      protected image call() throws exception {
        return imagerepo.getimage(record).orelsethrow();
      }

      @override
      protected void succeeded() {
        onsuccess.accept(getvalue());
      }

      @override
      protected void failed() {
        getexception().printstacktrace();
      }
    };
  }
}
登录后复制

imagerepository.java

package com.example;

import static java.sql.statement.return_generated_keys;

import java.io.bytearrayinputstream;
import java.io.bytearrayoutputstream;
import java.io.ioexception;
import java.io.inputstream;
import java.io.uncheckedioexception;
import java.sql.sqlexception;
import java.util.arraylist;
import java.util.list;
import java.util.objects;
import java.util.optional;
import java.util.concurrent.atomic.atomicinteger;
import javafx.embed.swing.swingfxutils;
import javafx.scene.image.image;
import javax.imageio.imageio;

public class imagerepository {

  private static final string select_all_records_sql = "select id, name from images";
  private static final string select_image_sql = "select image from images where id = ?";
  private static final string insert_sql = "insert into images (name, image) values (?, ?)";

  private final atomicinteger generatednamecount = new atomicinteger();
  private final imagesdatabase db;

  public imagerepository(imagesdatabase db) {
    this.db = db;
  }

  public list<imagerecord> getrecords() throws sqlexception {
    return db.execute(
        conn -> {
          try (var stat = conn.createstatement()) {
            var result = stat.executequery(select_all_records_sql);

            var records = new arraylist<imagerecord>();
            while (result.next()) {
              int id = result.getint(1);
              var name = result.getstring(2);
              records.add(new imagerecord(id, name));
            }
            return records;
          }
        });
  }

  public optional<image> getimage(imagerecord record) throws sqlexception {
    return getimage(record.id());
  }

  public optional<image> getimage(int recordid) throws sqlexception {
    if (recordid <= 0) {
      throw new illegalargumentexception("recordid <= 0: " + recordid);
    }
    return db.execute(
        conn -> {
          try (var stat = conn.preparestatement(select_image_sql)) {
            stat.setint(1, recordid);

            var result = stat.executequery();
            if (result.next()) {
              var image = new image(result.getbinarystream(1));
              return optional.of(image);
            } else {
              return optional.empty();
            }
          }
        });
  }

  public imagerecord insertimage(image image) throws sqlexception {
    objects.requirenonnull(image);
    return db.execute(
        conn -> {
          try (var stat = conn.preparestatement(insert_sql, return_generated_keys)) {
            var name = getimagename(image);

            stat.setstring(1, name);
            stat.setbinarystream(2, imagetoinputstream(image));
            stat.executeupdate();

            var keys = stat.getgeneratedkeys();
            if (keys.next()) {
              int id = keys.getint(1);
              return new imagerecord(id, name);
            } else {
              throw new illegalstateexception("generated key not returned");
            }
          }
        });
  }

  private string getimagename(image image) {
    var source = image.geturl();
    return source == null ? generateimagename() : source;
  }

  private string generateimagename() {
    return "generated image name " + generatednamecount.incrementandget();
  }

  private inputstream imagetoinputstream(image image) {
    var out = new bytearrayoutputstream();
    try {
      imageio.write(swingfxutils.fromfximage(image, null), "png", out);
    } catch (ioexception ex) {
      throw new uncheckedioexception(ex);
    }
    return new bytearrayinputstream(out.tobytearray());
  }
}
登录后复制

imagesdatabase.java

package com.example;

import java.sql.connection;
import java.sql.sqlexception;
import java.util.objects;
import java.util.concurrent.locks.lock;
import java.util.concurrent.locks.reentrantlock;
import javax.sql.datasource;
import org.h2.jdbcx.jdbcdatasource;

public class imagesdatabase implements autocloseable {

  private static final string create_table_sql =
      "create table images (id identity, name varchar(255), image blob)";

  @functionalinterface
  public interface sqlfunction<t> {

    t execute(connection connection) throws sqlexception;
  }

  private final lock mutex = new reentrantlock();

  private final datasource source;
  private connection connection;
  private boolean open = true;
  private boolean initialized;

  public imagesdatabase(string name) {
    if (name.isblank()) {
      throw new illegalargumentexception("blank name");
    }
    var source = new jdbcdatasource();
    source.seturl("jdbc:h2:mem:" + name + ";db_close_delay=-1");
    this.source = source;
  }

  public <t> t execute(sqlfunction<t> function) throws sqlexception {
    objects.requirenonnull(function);
    mutex.lock();
    try {
      checkopen();
      return function.execute(getoropenconnection());
    } finally {
      mutex.unlock();
    }
  }

  private connection getoropenconnection() throws sqlexception {
    if (connection == null || connection.isclosed()) {
      connection = source.getconnection();
      initialize(connection);
    }
    return connection;
  }

  private void initialize(connection conn) throws sqlexception {
    if (!initialized) {
      try (var stat = conn.createstatement()) {
        stat.executeupdate(create_table_sql);
      }
      initialized = true;
    }
  }

  private void shutdown() throws sqlexception {
    if (initialized) {
      try (var conn = getoropenconnection();
          var stat = conn.createstatement()) {
        stat.execute("shutdown");
      }
      connection = null;
    }
  }

  private void checkopen() {
    if (!open) {
      throw new illegalstateexception("closed");
    }
  }

  @override
  public void close() throws sqlexception {
    mutex.lock();
    try {
      if (open) {
        open = false;
        shutdown();
      }
    } finally {
      mutex.unlock();
    }
  }
}
登录后复制

gradle 文件

我使用了 kotlin dsl,但如果您愿意,您也可以使用 groovy dsl。

settings.gradle.kts

rootproject.name = "h2images-example"
登录后复制

build.gradle.kts

plugins {
    id("org.openjfx.javafxplugin") version "0.1.0"
    application
}

group = "com.example"
version = "1.0"

javafx {
    modules("javafx.controls", "javafx.swing")
    version = "21.0.1"
}

application {
    mainclass.set("com.example.main")
}

repositories {
    mavencentral()
}

dependencies {
    implementation("com.h2database:h2:2.2.224")
}
登录后复制

执行

您可以使用以下命令执行上述内容:

./gradlew run
登录后复制

注意 ./gradlew 调用 Gradle Wrapper。如果您的计算机上安装了 gradle 版本,则可以通过以下方式生成版本 8.4 的包装器:

gradle wrapper --gradle-version 8.4
登录后复制

1. javafx 在技术上不支持从类路径加载。这意味着理想情况下,javafx 模块应该位于模块路径上并解析为命名模块,即使您自己的代码和其他依赖项是从类路径加载的。但是,我不知道如果 javafx 位于类路径上(至少从 javafx 21 开始),会发生什么中断,除了您的主类不能再是 javafx.application 的子类。 application(您需要一个单独的“启动器类”作为主类)。只需要知道,由于 javafx 位于类路径上而导致的任何问题都不太可能被 javafx 团队修复。

请注意,openjfx 提供的 gradle 和 maven 插件会配置这些构建工具以将 javafx 放在模块路径上。

2. 根据您的两个问题的上下文,该示例将 image 对象转换为 png 字节。但是,如果您已经以字节形式接收图像(即作为本地或远程文件),那么将这些字节直接放入数据库可能会更容易、更高效。

以上是加载 JavaFX 图像并将其保存到数据库的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它们
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

比特币诞生至今历史价格总览 比特币历史价格趋势大全 比特币诞生至今历史价格总览 比特币历史价格趋势大全 Jan 15, 2025 pm 08:14 PM

比特币,作为一种加密货币,自问世以来经历了显着的市场波动。本文将提供比特币自诞生以来的历史价格总览,帮助读者了解其价格趋势和关键时刻。通过分析比特币的历史价格数据,我们可以了解市场对其价值评估、影响其波动的因素,并为未来投资决策提供依据。

比特币诞生至今历史价格一览 BTC历史价格行情趋势图(最新汇总) 比特币诞生至今历史价格一览 BTC历史价格行情趋势图(最新汇总) Feb 11, 2025 pm 11:36 PM

比特币自 2009 年创世以来,价格经历多次大幅波动,最高涨至 2021 年 11 月的 69,044.77 美元,最低跌至 2018 年 12 月的 3,191.22 美元。截至 2024 年 12 月,最新价格突破 100,204 美元。

2018-2024年比特币最新价格美元大全 2018-2024年比特币最新价格美元大全 Feb 15, 2025 pm 07:12 PM

实时比特币美元价格 影响比特币价格的因素 预测比特币未来价格的指标 以下是 2018-2024 年比特币价格的一些关键信息:

H5页面制作是前端开发吗 H5页面制作是前端开发吗 Apr 05, 2025 pm 11:42 PM

是的,H5页面制作是前端开发的重要实现方式,涉及HTML、CSS和JavaScript等核心技术。开发者通过巧妙结合这些技术,例如使用&lt;canvas&gt;标签绘制图形或使用JavaScript控制交互行为,构建出动态且功能强大的H5页面。

如何通过CSS自定义resize符号并使其与背景色统一? 如何通过CSS自定义resize符号并使其与背景色统一? Apr 05, 2025 pm 02:30 PM

CSS自定义resize符号的方法与背景色统一在日常开发中,我们经常会遇到需要自定义用户界面细节的情况,比如调...

为什么inline-block元素会出现错位现象?如何解决这个问题? 为什么inline-block元素会出现错位现象?如何解决这个问题? Apr 04, 2025 pm 10:39 PM

关于inline-block元素错位显示的原因及解决方案在编写网页布局时,我们常常会遇到一些看似奇怪的显示问题。比...

如何通过JavaScript或CSS控制浏览器打印设置中的页首和页尾? 如何通过JavaScript或CSS控制浏览器打印设置中的页首和页尾? Apr 05, 2025 pm 10:39 PM

如何使用JavaScript或CSS控制浏览器打印设置中的页首和页尾在浏览器的打印设置中,有一个选项可以控制是否显�...

Flex布局下文字超出省略却撑开容器?如何解决? Flex布局下文字超出省略却撑开容器?如何解决? Apr 05, 2025 pm 11:00 PM

Flex布局下文字超出省略导致容器撑开的问题及解决方法在使用Flex...