首頁  >  文章  >  載入 JavaFX 映像並將其儲存到資料庫

載入 JavaFX 映像並將其儲存到資料庫

WBOY
WBOY轉載
2024-02-22 14:49:061180瀏覽

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中文網其他相關文章!

陳述:
本文轉載於:stackoverflow.com。如有侵權,請聯絡admin@php.cn刪除