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 应用程序。但是,如果不知道您如何配置项目以及如何运行项目,我们将无法告诉您您的设置具体出了什么问题。
立即学习“Java免费学习笔记(深入)”;
也就是说,您想要完成的总体目标确实是可能的。下面是一个示例,允许您浏览计算机上的图像并将其保存到内存中的 h2 数据库中。图像的 id 和名称被放入 tableview 中,其中包含一个带有“打开”按钮的列,可让您查看从内存数据库加载的图像。图像以 png 格式作为 blob 存储在数据库中,无论其原始格式如何。 swingfxutils 和 imageio 类用于将 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
在 comment 至 previous 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();
}
}
}
我使用了 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中文网其它相关文章!
java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号