Bild aus MS Access in byte-Array und zu BufferedImage

Hallo,

ich bin schon seit gestern Abend an einem “kleinen” Problem am Basteln, aber komme überhaupt nicht weiter. Vielleicht hat jmd von euch einen Ratschlag?

Ich habe Bilder in einer MS Access Datenbank gespeichert und möchte nun ein Bild auf dem Frame in meinem Programm ausgeben lassen. Bisher habe ich das Bild in ein byte-Array speichern lassen, dieses dann zu einem InputStream und weiter zu einem BufferedImage umgewandelt. Leider bekomme ich immer eine NullPointerException. Hier mal der Code:

public BufferedImage getBild(String oid, String bid)
{
	Connection con;
	Statement st;
	ResultSet rs;
	String query;
		
	ImageIcon ico = null;
	BufferedImage image = null;
				    	
	try
	{
		query = "SELECT O_Bild_" + bid + " FROM Fehlerort WHERE O_ID = '" + oid + "'";
			
		con = DriverManager.getConnection(db);
		st = con.createStatement();
		rs = st.executeQuery(query);
		   	
		if(rs.next())
		{
			byte[] imagedata = rs.getBytes(1);
				
			System.out.println(imagedata.toString());
				
			try
			{
				InputStream in = new ByteArrayInputStream(imagedata);
				image = ImageIO.read(in);
					
				//ico = new ImageIcon(imagedata);
				//image = new BufferedImage(999, 761, BufferedImage.TYPE_INT_ARGB);
				//image.getGraphics().drawImage(ico.getImage(), 0, 0, ico.getImageObserver());
				//System.out.println(image);
			}
				
			catch(Exception ex)
			{
				Toolkit.getDefaultToolkit().beep();
			}				
		}
			
        	rs.close();
	        st.close();
	        con.close();
	}
				
	catch(Exception e)
	{
		e.printStackTrace();
	}
		
	return image;
}

Kann mir da jmd. weiterhelfen? danke schonmal!

Im Code zumindest keine Fehler erkennbar. Stilistisch würde ich einiges anders machen, z.B. ist Dein Exception-Handling recht unglücklich, angefangen mit dem catchen von Exception bis zu der Tatsache, dass Du Dein Programm einfach weiterlaufen lässt, auch wenn etwas schief gegangen ist.

Schaue, an welcher Stelle des Codes die NullPointerException fliegt. Damit siehst Du, auf “welches null” illegalerweise zugegriffen wurde. Mit dem Wissen, was da null war, kannst Du dann den Code untersuchen, der unerwarteterweise dieses null erzeugt hat und warum.

Hallo,

danke für deinen Tipp!

Ich muss dazu sagen, dass ich die Methode in der verschlankten Version gepostet habe, daher das mangelhafte Exception :slight_smile: Also die NullPointerException ensteht in der nachfolgenden Klasse, wenn ein BufferedImage versucht, das erhaltene Image (also das welches in dieser Methode getBild aus der DB geladen und zurückgegeben wird) auf eine kleinere Größe zu ändern. Liegt es vll. auch an der Art, wie das Bild in MS Access gespeichert wurde? Habe es als OLE-Bild gespeichert und wird in der DB als „Package“ angezeigt.

Ignoriere ich die Methode und übergebe dem BufferedImage den absoluten Pfad des Bildes auf der Festplatte, dann wird mir das Bild angezeigt. Es muss (so zumindest aus meiner Sicht) also irgendwie mit der Datenbank und dem byte-Array zusammenhängen bzw. wie vom byte-Array ein BufferedImage gespeichert wird.

Also gibt Deine Methode -wenn ich das richtig verstanden habe- null zurück?

  1. Untersuche rs.next() (entweder System.out.println oder mit Debugger). Vielleicht liefert die Query einfach nur kein Result. Setze die Query manuell in Access ab, um das zu überprüfen. Korrigiere den Querystring ggf. (Der dynamische Spaltennamen kommt mir z.B. merkwürdig vor.)
  2. Ersetze das „beep“ durch ein printStacktrace. Damit werden wenigstens alle geworfenen Exceptions ausgegeben. Schaue, ob bereits beim Zugriff auf die DB etwas schief gegangen ist.

Hey,
also ich habe das Coding mal um eine Konsolenausgabe des Arrays erweitert und auch das printStackTrace eingefügt.

Hier mal meine Ergebnisse zu deinen Tipps:
[ul]
[li]Das ResultSet habe ich mit System.out.println(rs.getObject(1)); geprüft und erhalte als Konsolenergebnis net.ucanaccess.jdbc.UcanaccessBlob@3e3fd5a2. Dies bedeutet doch, dass ein Ergebnis vorliegt, oder? Meine DB-Connection funktioniert eigentlich, da ich auch andere Methoden drauf connecten lasse, welche nur Text oder Zahlen liefern.[/li][li]Ja, die Abfrage habe ich schon in Access geprüft und habe auch ein Ergebnis erhalten. Zwar nur ein “Package”, wo ich aber mit Doppelklick das Bild erhalten habe. Bin mir daher nicht sicher, ob beim DB-Abruf wirklich die Bilddatei übergeben wird oder nur eine Verlinkung, sodass das Bild deshalb nicht angezeigt wird.[/li][li]Der Spaltenname hat folgenden Grund: In der Maske wählt man das Bauteil und das Modell per JComboBox aus. Bauteilnummer (oid) und Modellnummer (bid) werden dann übergeben und das passende Bild wird dann aufgerufen. Es gibt nur zwei Modelle, daher hat ein Datensatz eines Bauteils zwei Spalten mit jeweils den passenden Bildern.[/li][li]Ja, wenn die Methode aufgerufen wird, wird eine NullPointerException ausgelöst an der Stelle, wo das Bild das erste Mal verwendet werden soll.[/li][/ul]

Hier mal das angepasste Coding.

public BufferedImage getBild(String oid, String bid)
{
	Connection con;
	Statement st;
	ResultSet rs;
	String query;
		
	ImageIcon ico = null;
	BufferedImage image = null;
				    	
	try
	{
		query = "SELECT O_Bild_" + bid + " FROM Fehlerort WHERE O_ID = '" + oid + "'";
			
		con = DriverManager.getConnection(db);
		st = con.createStatement();
		rs = st.executeQuery(query);
		   	
		if(rs.next())
		{
			byte[] imagedata = rs.getBytes(1);
				
			System.out.println(rs.getObject(1));
			System.out.println(imagedata);
				
			try
			{
				InputStream in = new ByteArrayInputStream(imagedata);
				image = ImageIO.read(in);
			}
				
			catch(Exception ex)
			{
				ex.printStackTrace();
				Toolkit.getDefaultToolkit().beep();
				JOptionPane.showMessageDialog(new JFrame(), "Kein Bild für diese Fehlerort/Baureihen-Kombination vorhanden.
Bitte wenden Sie sich an den Administrator über 'Hilfe' -> 'Kontakt'.", "Fehler", JOptionPane.ERROR_MESSAGE);
			}				
		}
			
		rs.close();
	        st.close();
	        con.close();
	}
				
	catch(Exception e)
	{
		e.printStackTrace();
	}
		
	return image;
}

Und der Code aus der anderen Klasse, wo der Fehler ausgelöst wird:

Datenbankschnittstelle db = new Datenbankschnittstelle();
BufferedImage img = db.getBild(oid, bid); // Bild wird aus DB geholt
        
imageResized = img.getScaledInstance((int) bildBreite, (int) bildHoehe, Image.SCALE_SMOOTH); // Fehler wird ausgelöst
bufferedImageOptimized = new BufferedImage((int) bildBreite, (int) bildHoehe, BufferedImage.TYPE_INT_ARGB);
bufferedImageOptimized.getGraphics().drawImage(imageResized, 0, 0, null);

Hier mal die Fehlerausgabe. Bilder.java:114 ist hier der Aufruf " imageResized = img.getScaledInstance((int) bildBreite, (int) bildHoehe, Image.SCALE_SMOOTH); // Bild wird nicht angezeigt".


net.ucanaccess.jdbc.UcanaccessBlob@3e3fd5a2
[B@e0e47b4
java.lang.NullPointerException
	at risikoverwaltung.model.Bilder.<init>(Bilder.java:114)
	at risikoverwaltung.view.RisikoGUI.actionPerformed(RisikoGUI.java:667)
	at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
	at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
	at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
	at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
	at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
	at java.awt.Component.processMouseEvent(Unknown Source)
	at javax.swing.JComponent.processMouseEvent(Unknown Source)
	at java.awt.Component.processEvent(Unknown Source)
	at java.awt.Container.processEvent(Unknown Source)
	at java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
	at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
	at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Window.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
	at java.awt.EventQueue.access$400(Unknown Source)
	at java.awt.EventQueue$3.run(Unknown Source)
	at java.awt.EventQueue$3.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
	at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
	at java.awt.EventQueue$4.run(Unknown Source)
	at java.awt.EventQueue$4.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.WaitDispatchSupport$2.run(Unknown Source)
	at java.awt.WaitDispatchSupport$4.run(Unknown Source)
	at java.awt.WaitDispatchSupport$4.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.awt.WaitDispatchSupport.enter(Unknown Source)
	at java.awt.Dialog.show(Unknown Source)
	at java.awt.Component.show(Unknown Source)
	at java.awt.Component.setVisible(Unknown Source)
	at java.awt.Window.setVisible(Unknown Source)
	at java.awt.Dialog.setVisible(Unknown Source)
	at risikoverwaltung.view.RisikoGUI.<init>(RisikoGUI.java:586)
	at risikoverwaltung.view.LadeGUI.actionPerformed(LadeGUI.java:1781)
	at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
	at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
	at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
	at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
	at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
	at java.awt.Component.processMouseEvent(Unknown Source)
	at javax.swing.JComponent.processMouseEvent(Unknown Source)
	at java.awt.Component.processEvent(Unknown Source)
	at java.awt.Container.processEvent(Unknown Source)
	at java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
	at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
	at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Window.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
	at java.awt.EventQueue.access$400(Unknown Source)
	at java.awt.EventQueue$3.run(Unknown Source)
	at java.awt.EventQueue$3.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
	at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
	at java.awt.EventQueue$4.run(Unknown Source)
	at java.awt.EventQueue$4.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)

Vielen Dank nochmal!

*** Edit ***

Ich hab nochmal geprüft: Alles, was nach image = ImageIO.read(in); mit image passieren soll, wirft eine NullPointerException, obwohl der InputStream in einen Inhalt besitzt…

Schreib mal "if (img==null) {System.out.println(“img ist null”);} vor dein getScaledInstance. Dann hast du gewissheit ob deine Methode null zurückgibt oder nicht.
Probiers mal so wie hier: http://stackoverflow.com/questions/16212169/converting-blob-to-an-image-stream-and-assign-it-to-jlabel
Nicht gleich per getBytes lesen sondern das BLOB auslesen und von dem dann die bytes holen. Vielleicht hilfts.

Danke, habs mal ausprobiert mit der if-Abfrage und das Image ist tatsächlich null :frowning: Ich schaue mir mal den von dir geposteten Link an! Danke!

*** Edit ***

So, also mit dem abgeänderten Coding bekomme ich wenigstens keine Fehlermeldung mehr und das neue Fenster, wo das Bild erscheinen soll, öffnet sich auch. Leider ist dort immer noch kein Bild zu sehen - nur die Hintergrundfläche des Frames.

public BufferedImage getBild(String oid, String bid)
{
	Connection con;
	Statement st;
	ResultSet rs;
	String query;
		
	Image image = null;
	ImageIcon icon = null;
	BufferedImage buffImage = null;
	    	
	try
	{
		query = "SELECT O_Bild_" + bid + " FROM Fehlerort WHERE O_ID = '" + oid + "'";
			
		con = DriverManager.getConnection(db);
		st = con.createStatement();
		rs = st.executeQuery(query);
		   	
		if(rs.next())
		{
			byte[] imageData = rs.getBytes("O_Bild_" + bid);
				
			try
			{
				image = Toolkit.getDefaultToolkit().createImage(imageData);
		                icon = new ImageIcon(image);
		                buffImage = new BufferedImage(999, 761, BufferedImage.TYPE_INT_ARGB); 
		                buffImage.getGraphics().drawImage(icon.getImage(), 0,0, icon.getImageObserver());
					
		                //InputStream in = new ByteArrayInputStream(imagedata);
				//image = ImageIO.read(in);
					
				//ico = new ImageIcon(imagedata);
				//image = new BufferedImage(999, 761, BufferedImage.TYPE_INT_ARGB);
				//image.getGraphics().drawImage(ico.getImage(), 0, 0, ico.getImageObserver());
				//System.out.println(image);
			}
				
			catch(Exception ex)
			{
				ex.printStackTrace();
				Toolkit.getDefaultToolkit().beep();
				JOptionPane.showMessageDialog(new JFrame(), "Kein Bild für diese Fehlerort/Baureihen-Kombination vorhanden.
Bitte wenden Sie sich an den Administrator über 'Hilfe' -> 'Kontakt'.", "Fehler", JOptionPane.ERROR_MESSAGE);
			}				
		}
			
		rs.close();
	        st.close();
	        con.close();
	}
				
	catch(Exception e)
	{
		e.printStackTrace();
	}
		
	return buffImage;
}

Hast du es so schon probiert?


                int blobLength = (int) blob.length();

                byte[] blobAsBytes = blob.getBytes(1, blobLength);
                final BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(blobAsBytes));

Hi,
dann erhalte ich wieder eine NullPointerException an der bekannten Stelle :suspect: Kann doch nicht sein, dass das Bild nicht angezeigt wird, obwohl von der Logik her alles klappen sollte…

„Kann doch nicht sein, dass das Bild nicht angezeigt wird, obwohl von der Logik her alles klappen sollte…“
man, wie wär die Welt schöner wenn nicht immer solche Füll-Sätze dazwischen :wink:

was ist das byte[] eigentlich, wie lang, welcher Dateityp,
speichere es mit einem ByteArrayOutputStream auf die Festplatte mit richtigen Namen, kann man es danach normal öffnen?

wenn nein dann hast du weiter mit dem byte[] zu tun, versuche z.B. auf anderem Wege an die Originaldatei zu kommen, vergleiche die bytes usw.,
und bei all deinen Postings ist noch gar nicht so klar wieviel du überhaupt aus der DB liest,
bestenfalls ein byte[] != null (zwischenzeitlich… ob auch aktuell immer genau im Blick?), aber das könnte ja auch Länge 0, leer sein usw…


sobald die Datei fertig ist, evtl. auch einfach ein anderes beliebiges Bild gleichen Typs von irgendwoher holen, kann vorerst alles zur DB komplett vergessen werden,
lies das byte[] von der Festplatte, image != null ist auch wieder ein erster Test, aber noch nicht komplett aussagefähig, width & height abfragen zu richtigen Werten (vorher mit Bildprogramm herausfinden falls noch nicht bekannt) ist schon viel überzeugender,

beim Einbinden des Bildes in Swing GUI können auch für sich Fehler auftreten, eins nach dem anderen

wenn nach und nach alle Schritte einzeln genauer angeschaut, dann kann man sie verbinden,
natürlich sollte es letztlich möglich sein, das byte[] aus der DB zu verwenden, keine dauerhafte Festplatten-Zwischenspeicherung,
im Moment aber hilfreich (falls nicht noch mehr verwirrend, geht ja auch…)

Und wo genau wäre die bekannte Stelle? Wir kriegst du eigentlich deine Bilder in die Datebank? Da muss schon etwas mehr von dir kommen wenn ma ndir helfen soll. Z.B ein KSKB.

Hi,
danke für eure Hinweise!

Ich habe mal versucht, das byte-Array als Bild auf die Festplatte zu speichern.

if(rs.next())
{
	byte[] imageData = rs.getBytes("O_Bild_" + bid);
								
	try
	{
		image = Toolkit.getDefaultToolkit().createImage(imageData);
		icon = new ImageIcon(image);
		buffImage = new BufferedImage(999, 761, BufferedImage.TYPE_INT_ARGB); 
		buffImage.getGraphics().drawImage(icon.getImage(), 0, 0, icon.getImageObserver());
		            
		BufferedImage imae = ImageIO.read( new ByteArrayInputStream( imageData ) );
		ImageIO.write(imae, "JPG", new File("filename.jpg"));
	}
				
	catch(Exception ex)
	{
		ex.printStackTrace();
		Toolkit.getDefaultToolkit().beep();
		JOptionPane.showMessageDialog(new JFrame(), "Kein Bild für diese Fehlerort/Baureihen-Kombination vorhanden.
Bitte wenden Sie sich an den Administrator über 'Hilfe' -> 'Kontakt'.", "Fehler", JOptionPane.ERROR_MESSAGE);
	}				
}

Ich erhalte dann bei ImageIO.write eine NullPointerException, dass image == null! sei.


java.lang.IllegalArgumentException: image == null!
	at javax.imageio.ImageTypeSpecifier.createFromRenderedImage(Unknown Source)
	at javax.imageio.ImageIO.getWriter(Unknown Source)
	at javax.imageio.ImageIO.write(Unknown Source)
	at risikoverwaltung.control.Datenbankschnittstelle.getBild(Datenbankschnittstelle.java:924)
	at risikoverwaltung.model.Bilder.<init>(Bilder.java:117)
	at risikoverwaltung.view.RisikoGUI.actionPerformed(RisikoGUI.java:678)
	at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
	at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)

Die Bilder habe ich in die MS Access DB über das MS Access 2013 GUI eingefügt als OLE Objekt. Per Rechtsklick auf das Feld in der Tabelle und dann “Objekt einfügen”. Dann steht im Feld “Package”. Wenn ich die Quelldateien der Bilder lösche, kann ich die Bilder in der DB trotzdem öffnen, somit sind sie meiner Meinung nach auch in der DB enthalten (habe ja zeitweise auch daran gezweifelt, ob sie importiert wurden oder nur verknüpft).

Ich kann halt auch direkt die JPG Dateien von der Festplatte ziehen (ohne die aus der DB zu holen). Dann werden die Bilder ohne Probleme im Frame angezeigt. Es ist jedoch sehr wichtig, dass diese in der DB liegen und von da geholt werden. Ich schaue mal und setze mich mal mit dem byte Array weiter auseinander!

*** Edit ***

Das byte-Array hat die Länge 99053

Und du meinst du kannst ein OLE Objekt einfach so 1:1 in Byteform in ein BufferedImage übernehmen? Das geht sicher nicht. Zum glück bist du nicht der erste mit dem Problem: http://www.douglaspasqua.com/2013/01/24/java-remove-ole-headers-from-images-stored-as-ole-objects-in-access-database/

wie ich schrieb sollst du (für meinen Vorschlag, nicht allgemein sollen :wink: ) das byte speichern, mit FileOutoutStream etwa, ByteArrayOutputStream war Quark,
die Image-Klassen aber auch nicht beteiligen, wenn die gingen, dann wärst du ja schon fertig

99053er Array, auch befüllt? welche Datei wird daraus erzeugt, vergleiche sie mit dem Bild ‚ohne die aus der DB zu holen‘, beides anzeigbares Bild usw?

wie liest du diese Bilder von der Festplatte ein?
wahrscheinlich nicht über Umweg byte, das hilft noch nicht ganz weiter, obwohl es vielleicht die Möglichkeit eröffnet, byte aus DB zu laden, in temporäre Datei zu speichern, und dann erfolgreich anzuzeigen,

die Ziel sollte/ könnte es sein, wenn die Bilddatei auf der Festplatte liegt, die DB zu vergessen, das byte von der Festplatte zu holen und dann von demselben Problem zu stehen,
nicht unbedingt gleich mit Lösung, aber die wichtige Erkenntnis ist, dass die DB (immerhin Thementitel hier!) dann gar nichts mehr mit dem Problem zu tun hat,
solche Erkenntnisse sind wichtig (wie gesagt aber auch gut möglich dass ich mehr verwirre mit dem hin und her, nicht blind folgen, nur dann wenn auch bisschen durchdacht, was ich bezwecke)


wenn Bild von Festplatte mit Stream geht dann sollte auch Bild aus ByteArrayInputStream auf ein byte gehen,
das musst du in der Tat genauer testen

Ich werd bekloppt! Es funktioniert nun! YAY =D Ich hab nicht gedacht, dass die OLE Header solche Probleme verursachen können. Habe nun die OLE-Remove-Klasse ausm Link hinzugefügt und es klappt nun einwandfrei. Danke euch für eure Hilfe. Ein schönes Wochenende euch beiden!