Java Class zum komprimieren von Jpeg-Images zum Einbinden in Oracle DB - Bild als blob (in und out)

hkoelzer

Mitglied
Hey,

erstmal vorweg: Ich bin Datenbankprogrammierer im Oracleumfeld und habe so gut wie keine Ahnung von Java (mal abgesehen von meinen Erkenntnissen der letzten 5 Tage ;-)

Hintergrund meiner Anliegens: Wir verwenden derzeit in der Oracle Datenbank (19.1) ein Programm von Oracle selber (ordimage), um Bilder zu konvertieren und und teils zu komprimieren.
Diese Programm wird in den neuen Versionen der Datenbank nicht mehr zu Verfügung stehen. Daher suchen wir nach Alternativen.
Da man Java in die Datenbank einbinden kann und dann keine weiteren Installationen oder kostenpflichtige Programme benötigen würde, ist unsere Wahl auf Java gefallen.

Ich habe auch schon rumgegoogled und auch schon ein Programm gefunden, was die die Konvertierung vornimmt und mit einem blob als Parameter(rein und raus) funktioniert.
Ich habe auch schon ein Programm gefunden (https://examples.javacodegeeks.com/desktop-java/imageio/compress-a-jpeg-file/) welches die komprimierung vornehmen kann. Dies setzt aber ein Bild auf dem Filesystem voraus und schreibt auch das komprimierte Bild wieder ins Filesystem. Das habe ich lokal bei mir auch ausprobiert und das erzeugte neue Bild ist auch kompremiert.
Ich habe versucht das Programm so anzupassen, das er mit blob zurecht kommt und auch einen blob mit dem komprimierten Image zurückgibt.


Um mein Programm lokal auf meinem Rechner Test zu können, hole ich ein jpg-Bild aus meiner Datenbank als Blob über odbc und rufe dann compressJPEG (myBlobCopy) mit dem Blob auf. Um zu zeigen, was passiert ist, mache ich einige Ausgaben in den Klassen. Die Größe des BLOBs vor und nach dem Aufruf von compressJPEG ist gleich.

Hier die Ausgabe: 126 23190 myimage.jpg Länge des abgerufenen Blob: 4284416

Länge des kopierten Blob: 4284416

Aufruf von compressJPEG (myBlobCopy)

jetzt in compressJPEG

Zurück von compressJPEG: Länge des abgerufenen Blob: 4284416

Hier die Klasse. Es scheint, dass das komprimierte Bild (Blob) nicht zurückgegeben wird. Bitte können Sie mir helfen!!!!!!

[CODE lang="java" title="Java Klasse"] import java.sql.*;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.sql.Blob;
import java.i😵utputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.i😵utputStream;
import java.util.Iterator;
import javax.imageio.IIOImage;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
//import java.sql.SQLException;
class OracleConCompressBLOB{
public static void main(String args[]){
try{
//step1 load the driver class
Blob myBlob = null;
Blob myBlobCopy = null;

Class.forName("oracle.jdbc.driver.OracleDriver");
String dbURL = "jdbc😱racle:thin😡(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = xxxx)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED)(SERVICE_NAME = xxxx) ) )";
String strUserID = "xxxx";
String strPassword = "xxxxx";
//step2 create the connection object

Connection con=DriverManager.getConnection(dbURL,strUserID,strPassword);


//step3 create the statement object
Statement stmt=con.createStatement();

//step4 execute query
// fuer pbent ResultSet rs=stmt.executeQuery("select PRFO_ID, PRFO_PRAX_ID , PRFO_DATEINAME, PRFO_FOTO from T_PRAFOTO where PRFO_ID = 17");
ResultSet rs=stmt.executeQuery("select PRFO_ID, PRFO_PRAX_ID , PRFO_DATEINAME, PRFO_FOTO from T_PRAFOTO where PRFO_ID = 166 FOR UPDATE");


if (rs.next()) {
myBlob = rs.getBlob(4);
myBlobCopy = myBlob;
System.out.println(rs.getInt(1)+" "+rs.getInt(2)+" "+rs.getString(3)+" Length of retrieved Blob: " + myBlob.length());
System.out.println(" Length of copy Blob: " + myBlobCopy.length());
System.out.println("Call compressJPEG (myBlobCopy) ");
compressJPEG (myBlobCopy) ;
System.out.println("back from compressJPEG: Length of retrieved Blob: " + myBlobCopy.length());
}


//step5 close the connection object
con.close();

}catch(Exception e){ System.out.println(e);}

}


public static void compressJPEG(Blob blob) throws IOException {

// File imageFile = new File("myimage.jpg");
// File compressedImageFile = new File("myimage_compressed.jpg");

// InputStream is = new FileInputStream(imageFile);
// OutputStream os = new FileOutputStream(compressed ImageFile);
System.out.println("now in compressJPEG");

BufferedImage bufferedImage = null;
OutputStream outputStream = null;
float quality = 0.5f;
try {
// create a BufferedImage as the result of decoding the supplied InputStream
// BufferedImage image = ImageIO.read(is);
bufferedImage = ImageIO.read(blob.getBinaryStream());
outputStream = blob.setBinaryStream(0);
// test
// get all image writers for JPG format
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg");

if (!writers.hasNext())
throw new IllegalStateException("No writers found");

ImageWriter writer = (ImageWriter) writers.next();
ImageOutputStream ios = ImageIO.createImageOutputStream(outputStream);

writer.setOutput(ios);

ImageWriteParam param = writer.getDefaultWriteParam();

// compress to a given quality
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(quality);

// appends a complete image stream containing a single image and
//associated stream and image metadata and thumbnails to the output
writer.write(null, new IIOImage(bufferedImage, null, null), param);

// close all streams
// is.close();
// os.close();
// ios.close();
// writer.dispose();
outputStream.flush();
ios.close();
outputStream.close();
writer.dispose();
} catch (IOException e) {
e.printStackTrace();
}
catch (SQLException e) {
e.printStackTrace();
}
catch(IllegalArgumentException e) {
e.printStackTrace();
}

}
} [/CODE]
 
Meinst du, ich soll der Klasse public static void compressJPEG(Blob blob)
einen Returnwert Blob geben, also eine Funktion daraus machen?

Oder was meinst du? Dann vielleicht am besten mit Beispiel, wie gesagt, Java Dummie ;-)
 
myBlobCopy = myBlob ist kein deep Copy da hast wahrseinlcih den Fehler

Java:
        while ((line = br.readLine()) != null) {
            File file = new File("/myPath/" + name + ".png");
            FileInputStream fis = new FileInputStream(file);
            ps.setBinaryStream(2, fis, (int) file.length());
so kannst du png einlesen musst halt umändern auf deinen datentyp

Java:
  private void readImageFromServer(InputStream inputStream) throws IOException
  {

  byte byteStream[];
  File files=new File("/home/legacy/Bilder/image1.png");
  FileOutputStream fos=new FileOutputStream(files); byteStream =
  (inputStream.readAllBytes());
  fos.write(byteStream, 0, byteStream.length);
  fos.close(); }
so kannst du dir dann dein die datei Image1.png erzeugen von deinem inpuitstream den du oben hast dann kannst du deine datensätze hin und her werfen wie du willst
Java:
ResultSet rs = statement.executeQuery("select * from table");
while(rs.next())
{
readImageFromServer(rs.getBinaryStream(2));
}
closesConnection(ps);
fis.close();
die methode würde dir alle blobs auslesen diese kannst du dir dann in read einsetzen und dann irgendwo hin tun ..wo auch immer du es haben willst

das selecten sollte eig prepared sein aber ich war zu faul weil das sowieso nur zum ausprobieren war
 
Zuletzt bearbeitet von einem Moderator:
Nur um das klar zu stellen ein BLOB = Binary Large Object dh du wandelst deine jped datei um in ein Binary das kann unter umständen größer und kleiner sein als "komprimieren" verstehe ich eher Zip oder tar



nicht dass du jetzt mit blob rum haust und dann die datenbank admins dich umbringen weil du zu viel platz brauchst
 
Hey Mart,

erstmal danke für deine Anmerkungen, auch wenn ich zugegebener Massen damit jetzt nicht so richtig etwas anfangen kann.

1. myBlobCopy = myBlob ist kein deep Copy
Ich verstehe das mal so, das der BLOB nicht wirklich kopiert wird, sondern eventuell nur ein Zeiger auf das Objekt übergeben wird ?
Wie kann ich das ändern?

2. Wie helfen mir dann 2 Anmerkungen (Java Code) weiter - ich lese ja kein File vom Laufwerk, sondern haben einen BLOB?
Wenn meine Klasse ja mal funktioniert, rufe ich sie mit einem BLOB aus der Datenbank auf.
 
Hey Mart,

erstmal danke für deine Anmerkungen, auch wenn ich zugegebener Massen damit jetzt nicht so richtig etwas anfangen kann.

1. myBlobCopy = myBlob ist kein deep Copy
Ich verstehe das mal so, das der BLOB nicht wirklich kopiert wird, sondern eventuell nur ein Zeiger auf das Objekt übergeben wird ?
Wie kann ich das ändern?

2. Wie helfen mir dann 2 Anmerkungen (Java Code) weiter - ich lese ja kein File vom Laufwerk, sondern haben einen BLOB?
Wenn meine Klasse ja mal funktioniert, rufe ich sie mit einem BLOB aus der Datenbank auf.
die 3te anmerkungen liest dir einen Blob aus aus einer datenbank
die 2te Anmerkung wandelt einen blob in ein Jpeg
die 1te Anmerkung speichert ein jpeg als blob

deine aufgabe ist es nun den binary Inputstream wert weiter zu leiten in eine andere datenbank ...die lösungen msust du schon selber machen 😀

deep copy kannst du mit cloneable erreichen oder du erstellst einen neuen blob un der bekommt die werte die in den anderen blob drin stehen ...deep copy ist aber nicht sooooooo...

ich denke auch nicht dass du unbedingt eine copy brauchst
 
du kannst ja den blob komprimieren


und ja dein kopieren bezieht sich nur auf den pointer der dateien..du erzeugst kein neues objekt mit den gleichen werten sondern nur die pointer
 
Hallo Mart,

1-3 sind ja nicht meine Probleme.

Ich befinde mich ja vor Aufruf der Java Klasse in der Datebank.
Das jpeg ist in einer Tabelle mit einem blob gespeichert wo ich den blob ja abfragen kann, um diesen dann weiter an die java Klasse zu geben.
Von dort erwarte ich das umgewandelte jpeg wieder als blob und lege es wieder in der tabelle mit dem Spaltentyp blob ab.
Die Umwandlung der blob in ein Jpeg aus der Datebankheraus ist dann auch wieder kein Problem.
 
Hallo Mart,

1-3 sind ja nicht meine Probleme.

Ich befinde mich ja vor Aufruf der Java Klasse in der Datebank.
Das jpeg ist in einer Tabelle mit einem blob gespeichert wo ich den blob ja abfragen kann, um diesen dann weiter an die java Klasse zu geben.
Von dort erwarte ich das umgewandelte jpeg wieder als blob und lege es wieder in der tabelle mit dem Spaltentyp blob ab.
Die Umwandlung der blob in ein Jpeg aus der Datebankheraus ist dann auch wieder kein Problem.
also blob holen -> umwandeln in jpeg -> umwandeln in blob -> blob woanders speichern? ist das so richtig?
 
vielleicht muss ich etwas weiter ausholen - es geht mir nicht um Platz sparen in der Datenbank
Die JPG´s werden innerhalb eines pdf-Generieres in der Datebank in ein pdf eingebunden.
Bei bestimmten jpg´s schlägt das Generieren des pdf fehls, bzw. die Bilder werden da nicht angezeigt (was vermutlich ein Problem des PDF-Programms ist).
Es hat sich aber gezeigt, wenn wir das jpeg etwas komprimieren, ist die Generierung des pdf erfolgreich.
 
also der user speichert ein beschädigtes jpeg und sobald du so eins findest willst du es mit einem komprimierten jpeg austuaschen dass es wieder funktioniert?
 
damit wäre die idee -> blob holen -> in jpeg umwandeln -> komprimieren -> blob speichern

nur ist es nicht problematisch wenn du in einer tabelle 2 daten hast...1 mal komprimiert einmal unkomprimiert?
 
Ich glaube nicht, das das Bild beschädigt ist. Ich kann das Bild ja auch aufmachen. Nur dem pdf-Generator scheint irgendetwas nicht zu passen und zeigt dann nur einen Rahmen aber kein Bild im pdf an - eine Fehlermeldung gibt es dann auch nicht
Kommt auch nicht oft vor, aber es passiert halt.
Das komprimieren bekämpft also nur die Symptome. Die Ursache ist der pdf-Generator
 
Die Daten (das komprimierte Bild) wird dann nur einmal abgelegt. Der blob befindet sich vor der Konvertierung noch in einer temporären Tabelle.

Ja, bei stackoverflow habe ich mein Glück auch schon versucht.

Das Problem ist, das ich das mit dem blob benötige und nicht auf einem Filesystem bin und ich nicht genau weiß, wie es funktioniert.
 
meins liest blobs und kann blobs einlesen
du brauchst den datentyp blob nicht extra

meine Datenbank hat auch den blob als datentyp aber der input stream kann den als bytestream lesen und dann steht da der blob schon drin mach eifnach mal meins mit rs.getBinaryStream(2) anstatt dein rs.getBlob
 
Das ich eine odbc aufmache, soll ja in Wirklichkeit nicht passieren. Das war ja nur en Behelf, damit ich das lokal testen kann und auch mal Fehlermeldungen etc. sehen kann.
In Wirklichkeit rufe ich ja dann die Java Klasse compressJPEG(Blob blob) mit dem blob aus der Datenban direkt auf - alles was in main steht (odbc, select ..., war nur für den Test lokal)
 
Nur um das klar zu stellen ein BLOB = Binary Large Object dh du wandelst deine jped datei um in ein Binary das kann unter umständen größer und kleiner sein als "komprimieren" verstehe ich eher Zip oder tar
Ich habe keine Ahnung, ob ich dich auch nur ansatzweise richtig versteht, aber: ein JPG ist ein Binary, Blob ist einfach nur die Repräsentation der binär-Daten in der Datenbank, und JPG ist ein Kompressions-Verfahren/Format.

Ein JPG ist als Blob höchstens durch Metadaten größer als das eigentliche Bild, aber nicht plötzlich auf magische Art und Weise deutlich größer oder kleiner, und ein JPG ist immer komprimiert, das ist der ganze Sinn des Formats.

BTW: Zeichen wie Punkt und Komma gibt es auch gutem Grund, du solltest sie auch mal probiere 😉
 
Ich habe keine Ahnung, ob ich dich auch nur ansatzweise richtig versteht, aber: ein JPG ist ein Binary, Blob ist einfach nur die Repräsentation der binär-Daten in der Datenbank, und JPG ist ein Kompressions-Verfahren/Format.

Ein JPG ist als Blob höchstens durch Metadaten größer als das eigentliche Bild, aber nicht plötzlich auf magische Art und Weise deutlich größer oder kleiner, und ein JPG ist immer komprimiert, das ist der ganze Sinn des Formats.

BTW: Zeichen wie Punkt und Komma gibt es auch gutem Grund, du solltest sie auch mal probiere 😉
ich hatte ihn falsch verstanden gehabt und deswegen ergibt der satz von mir keinen Sinn mehr 😁
 

Zurück
Oben