Rumah >Java >Muatkan imej JavaFX dan simpannya ke pangkalan data

Muatkan imej JavaFX dan simpannya ke pangkalan data

WBOY
WBOYke hadapan
2024-02-22 14:49:061229semak imbas

JavaFX ialah kit alat antara muka pengguna grafik untuk bahasa pengaturcaraan Java yang memudahkan untuk mencipta antara muka pengguna yang kaya dan moden. Dalam pembangunan sebenar, kadangkala ia perlu memuatkan imej JavaFX dan menyimpannya ke pangkalan data. Artikel ini akan menunjukkan kepada anda cara melakukan ini dan membantu anda menyelesaikan masalah ini melalui langkah-langkah yang mudah dan mudah difahami. Mari kita bincangkan bersama!

Kandungan soalan

Saya sedang cuba menyimpan imej kecil dalam pangkalan data dan memuatkannya untuk digunakan sebagai javafx.scene.image.Image. Beberapa penyelesaian yang saya cuba melibatkan penggunaan SwingFXUtils.fromFXImage dan SwingFXUtils.toFxImage, tetapi kelas itu nampaknya memerlukan kemasukan dalam fail module-info.java yang saya tidak cuba gunakan pun.

Saya mengalami pengecualian ini apabila cuba menggunakan SwingFXUtils: java.lang.NoClassDefFoundError: javafx/embed/swing/SwingFXUtils.

Apabila saya menggunakan sistem modul, ia merosakkan perpustakaan lain yang saya gunakan (itextpdf). Jadi saya ingin mengelakkan ini dan hanya mencari cara lain untuk menyimpan dan memuatkan imej JavaFX daripada pangkalan data. Ada apa-apa cadangan?

Penyelesaian

Anda menyebut:

Ini tidak benar. Walaupun javafx hanya menyokong memuatkan 1 seperti yang dinamakan modul, javafx tidak memerlukan bahawa kod anda sendiri menjadi modular. Satu-satunya cara untuk mendapatkan noclassdeffounderror adalah dengan gagal memasukkan kelas dalam laluan modul/path kelas semasa runtime (ia mesti hadir pada masa penyusunan, jika tidak, kod anda tidak akan dikompil). Saya cadangkan membaca Bermula dengan JavaFX untuk mengetahui cara menyediakan aplikasi javafx asas menggunakan salah satu IDE java utama dan/atau alat binaan. Walau bagaimanapun, tanpa mengetahui cara anda mengkonfigurasi projek anda dan menjalankannya, kami tidak dapat memberitahu anda apa sebenarnya yang salah dengan persediaan anda.

Maksudnya, matlamat keseluruhan yang anda ingin capai sememangnya mungkin. Di bawah ialah contoh yang membolehkan anda menyemak imbas imej pada komputer anda dan menyimpannya ke pangkalan data h2 dalam memori. Id dan nama imej dimasukkan ke dalam kelas tableview 中,其中包含一个带有“打开”按钮的列,可让您查看从内存数据库加载的图像。图像以 png 格式作为 blob 存储在数据库中,无论其原始格式如何。 swingfxutilsimageio yang digunakan untuk menukar imej javafx kepada "fail" png 2.

Contoh tidak menunjukkan kepada anda cara menggunakan aplikasi (cth. melalui jpackage).

Versi

Berikut ialah versi perpustakaan dan alatan yang saya gunakan untuk membina dan menjalankan contoh.

  • java 21.0.1 (gerhana adoptium/temurin)

  • javafx 21.0.1

  • h2 2.2.224

  • darjah 8.4

  • JavaFX Gradle Plugin 0.1.0

Dalam komen kepada soalan sebelumnya2 anda menyatakan bahawa anda menggunakan gradle, jadi saya menggunakan gradle dalam contoh.

Kod sumber

Tiada module-info.java fail.

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();
    }
  }
}

fail gred

Saya menggunakan kotlin dsl, tetapi anda juga boleh menggunakan dsl groovy jika anda mahu.

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")
}

Laksanakan

Anda boleh melaksanakan perkara di atas menggunakan arahan berikut:

./gradlew run

Nota ./gradlew memanggil Gradle Wrapper. Jika anda mempunyai versi gradle yang dipasang pada mesin anda, anda boleh menjana pembalut untuk versi 8.4 melalui:

gradle wrapper --gradle-version 8.4

1. javafx secara teknikal tidak menyokong pemuatan daripada laluan kelas. Ini bermakna bahawa secara idealnya, modul javafx harus berada di laluan modul dan menyelesaikannya kepada modul yang dinamakan, walaupun kod anda sendiri dan kebergantungan lain dimuatkan daripada laluan kelas. Walau bagaimanapun, saya tidak tahu apa yang berlaku breaks jika javafx berada di classpath (sekurang-kurangnya pada javafx 21), kecuali kelas utama anda tidak boleh lagi javafx.application 的子类。 application (anda memerlukan "kelas pelancar" yang berasingan sebagai kelas utama ). Ketahuilah bahawa sebarang isu yang disebabkan oleh javafx berada di classpath tidak mungkin dapat diselesaikan oleh pasukan javafx.

Sila ambil perhatian bahawa pemalam gradle dan maven yang disediakan oleh openjfx mengkonfigurasi alat binaan ini untuk meletakkan javafx pada laluan modul.

2. Berdasarkan konteks dua soalan anda, contoh ini menukarkan image objek kepada bait png. Walau bagaimanapun, jika anda sudah menerima imej sebagai bait (iaitu sebagai fail setempat atau jauh), mungkin lebih mudah dan lebih cekap untuk meletakkan bait tersebut terus ke dalam pangkalan data.

Atas ialah kandungan terperinci Muatkan imej JavaFX dan simpannya ke pangkalan data. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:stackoverflow.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam
Artikel sebelumnya:Grid nyahpepijat untuk libgdxArtikel seterusnya:Grid nyahpepijat untuk libgdx