JavaFX est une boîte à outils d'interface utilisateur graphique pour le langage de programmation Java qui facilite la création d'interfaces utilisateur riches et modernes. Dans le développement réel, il est parfois nécessaire de charger des images JavaFX et de les enregistrer dans la base de données. Cet article vous expliquera comment procéder et vous aidera à résoudre ce problème grâce à des étapes simples et faciles à comprendre. Discutons-en ensemble !
J'essaie actuellement de sauvegarder une petite image dans une base de données et de la charger pour qu'elle soit utilisée comme javafx.scene.image.Image. Quelques solutions que j'ai essayées impliquent l'utilisation de SwingFXUtils.fromFXImage et SwingFXUtils.toFxImage, mais cette classe semble nécessiter une entrée dans le fichier module-info.java que je n'essaie même pas d'utiliser.
J'ai rencontré cette exception en essayant d'utiliser SwingFXUtils : java.lang.NoClassDefFoundError: javafx/embed/swing/SwingFXUtils
.
Lorsque j'ai utilisé le système de modules, cela a cassé une autre bibliothèque que j'utilisais (itextpdf). Je veux donc éviter cela et simplement trouver un autre moyen de sauvegarder et de charger des images JavaFX à partir de la base de données. Aucune suggestion?
Vous avez mentionné :
Ce n'est pas vrai. Bien que javafx ne prenne en charge que le chargement de 1 en tant que modules nommés, javafx n'exige pas que votre propre code soit modulaire. La seule façon d'obtenir est de ne pas inclure la classe dans le chemin du module/chemin de classe au moment de l'exécution (elle doit être présente au moment de la compilation, sinon votre code ne sera pas compilé). Je recommande de lire Getting Started with JavaFXnoclassdeffounderror
pour apprendre à configurer une application javafx de base à l'aide de l'un des principaux IDE Java et/ou outils de construction. Cependant, sans savoir comment vous configurez votre projet et l'exécutez, nous ne serons pas en mesure de vous dire exactement ce qui ne va pas avec votre configuration.
Cela dit, l’objectif global que vous souhaitez atteindre est bel et bien possible. Vous trouverez ci-dessous un exemple qui vous permet de rechercher des images sur votre ordinateur et de les enregistrer dans une base de données h2 en mémoire. L'identifiant et le nom de l'image sont placés dans la classe
tableview
中,其中包含一个带有“打开”按钮的列,可让您查看从内存数据库加载的图像。图像以 png 格式作为 blob 存储在数据库中,无论其原始格式如何。 swingfxutils
和 imageio
2.
L'exemple ne vous montre pas comment déployer l'application (par exemple via
jpackage
Version
à la question précédente2, vous avez déclaré que vous utilisiez gradle, j'utilise donc gradle dans l'exemple. Code source
module-info.java
package com.example;
public record imagerecord(int id, string name) {}
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();
}
};
}
}
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());
}
}
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();
}
}
}
rootproject.name = "h2images-example"
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
Note
appelle leGradle Wrapper./gradlew
. Si vous avez une version Gradle installée sur votre machine, vous pouvez générer un wrapper pour la version 8.4 via :
gradle wrapper --gradle-version 8.4
1. javafx ne prend pas techniquement en charge le chargement à partir du chemin de classe. Cela signifie qu'idéalement, le module javafx devrait se trouver sur le chemin du module et se résoudre en un module nommé, même si votre propre code et d'autres dépendances sont chargés à partir du chemin de classe. Cependant, je ne sais pas ce qui se passe breaks si javafx est sur le chemin de classe (au moins à partir de javafx 21), sauf que votre classe principale ne peut plus l'être (vous avez besoin d'une "classe de lancement" distincte comme classe principale ) . Sachez simplement qu'il est peu probable que les problèmes causés par la présence de javafx sur le chemin de classe soient résolus par l'équipe javafx. javafx.application 的子类。 application
2. Basierend auf dem Kontext Ihrer beiden Fragen konvertiert dieses Beispiel image
Objekte in PNG-Bytes. Wenn Sie das Bild jedoch bereits in Bytes erhalten (d. h. als lokale oder Remote-Datei), ist es möglicherweise einfacher und effizienter, diese Bytes direkt in die Datenbank zu übertragen.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!