Feike EDV Beratung München

Base64 kodierte Daten aus XML Dateien dekodieren und binär speichern in JAVA

XML ist überall. Will man Binärdaten wie bspw. JPG, GIF, PNG Bilder mit XML übertragen, ist BASE64 die erste Wahl bei der Kodierung. Auch bei binären EMail Attachments (z.B. PDF Dateien) hat sich BASE64 als der Standard Nummer 1 etabliert.
Im Folgenden zeige ich in einfachen Schritten, wie ein BASE64 kodierter Content aus einem XML File mittels JAVA extrahiert und anschließend als Binärdatei gespeichert wird. Das komplette Programm gibts am Ende des Artikels.


Die XML Datei laden und in ein DOM parsen

Ich verwende den JDOM SAXBuilder, man muss ja nicht ständig alles neu erfinden. Das Exceptionhandling spare ich mir der Übersichlichkeit halber. Der Name der XML Datei muss als erster Programmparameter angegeben sein (args[0]). SAXBuilder und Document sind Klassen aus org.jdom.
	SAXBuilder builder = new SAXBuilder();
	Document document = builder.build( args[0] );
Wenn die XML Datei wohlgeformt war, und auch sonst alles ordentlich über die Bühne ging, dann haben wir jetzt in "document" unser XML Dokument. Der Zugriff auf die Elemente erfolgt im DOM Objekt sehr gemütlich.

Das Element aus dem DOM extrahieren

Der Benutzer soll das zu extrahierende Base64-Element in einer XPath ähnlichen Notation angeben. Beispiel:
<ROOT>
   <PERSONAL>
      <BEWERBER>
         <PHOTO>
            ... G8+ucqUNx4DzTJ4vv1xz8QFnRVB9gj85He3jeve3p6MNc ...
         </PHOTO>
      </BEWERBER>
   </PERSONAL>
</ROOT>
Das BASE64 codierte Photo soll das Programm durch den Kommandozeilenparameter 3 in der Form "PERSONAL/BEWERBUNG/PHOTO" angeben. Also als Pfad ab dem Root-Element. Wir verwenden natürlich wieder die JDOM Bibliothek. Der Code (Achtung, wieder fehlt die Fehlerbehandlung):
	String path[] = args[2].split("/");
	Element element = document.getRootElement();
	int i = 0;
	while ((null != element) && (i < path.length)) {
		element = element.getChild(path[i++]);
	}
In element ist jetzt entweder "null" (Fehlerfall) oder das angegbene org.jdom.Element, dass unser BASE64 enthält (in Schema übrigens xs:base64Binary). Sollte auf dem Weg zum xs:base64binary Element ein Element mit Kardinalität größer eins auftauchen, wirds natürlich etwas problematischer. Das ist hier aber nicht Thema.

BASE64 konvertieren und speichern

Um den BASE64 Block zu dekodieren verwende ich den sun Decoder (wie gesagt, man soll nicht ständig das Rad neu erfinden), Der Decoder hat allerdings die Schwäche, das er Zeichen, die nicht im BASE64 Alphabet drin sind falsch umwandelt. Das führt beispielsweise dann zu Problemen, wenn man einen BASE64 String aus einem "ge-prettyfieden" XML verwendet. Die meisten Formatter fügen nämlich Blanks und Linebreaks in die Base64 Blöcke. Also Achtung, alle blanks aus dem String entfernen bevor der BASE64Decoder zum Einsatz kommt. Der Code (Achtung, wieder fehlt die Fehlerbehandlung):
	String base64 = element.getTextNormalize();
	byte decoded[] = new sun.misc.BASE64Decoder().decodeBuffer(base64);
Im byte[] decoded findet sich nun der binäre Content.

Binärdatei speichern

Die Bytes jetzt in eine Datei zu speichern ist in Java natürlich nicht schwer:
	FileOutputStream  fos = null;
	fos = new FileOutputStream(args[1]);
	fos.write(decoded);
	fos.close();

Und nun ein komplettes Programm für Copy & Paste Fans


import java.io.FileOutputStream;
import java.io.IOException;
import java.util.logging.Logger;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

public class Base64DecodeFromXML {

	static public Logger logger = Logger.getLogger("Base64DecodeFromXML");
	static private Document document = null;  // the doc

	/**
	 * @param args inputFile outputFile "path/to/base64element"
	 */
	public static void main(String[] args) {
		// startup
		logger.info("Base64DecodeFromXML 1.0 - starting up");
		if (args.length < 3) {
			logger.severe("Base64DecodeFromXML got not enough parameters - filenames needed");
			System.err.println("Usage: Base64DecodeFromXML inputFile outputFile path/to/base64element");
			System.exit(-1);
		}
		// load xml-doc into document object
		logger.info("loading input file " + args[0]);
		SAXBuilder builder = new SAXBuilder();
		// try to build the doc
		try {
			Base64DecodeFromXML.document = builder.build( args[0] );
		} catch (JDOMException e) {
			e.printStackTrace();
			document = null;
		} catch (IOException e) {
			e.printStackTrace();
			document = null;
		}
		if (null == Base64DecodeFromXML.document) {
			logger.severe("Base64DecodeFromXML got bad XML file - not exiting or not wellformed");
			System.exit(-1);
		}
		// extract the path to element that contains the base64 encoded binary from the commandline
		String path[] = args[2].split("/");
		// get the root element of the XML Doc
		Element ele = Base64DecodeFromXML.document.getRootElement();
		// recurse into the xml structure until we find the base64 element
		int i = 0;
		while ((null != ele) && (i < path.length)) {
			logger.info("iterating into " + path[i]);
			ele = ele.getChild(path[i]);
			if (null == ele) {
				logger.severe("Element not found: " + path[i]);
				break;
			}
			++i;
		}
		if (null != ele) {
			// get the base64 content into String
			String base64 = ele.getTextNormalize();
			
			// and decode the content
			try {
				byte decoded[] = new sun.misc.BASE64Decoder().decodeBuffer(base64);
				// save it to a binary stream
				logger.info("saving output file " + args[1]);
				FileOutputStream  fos = null;
				try {
					fos = new FileOutputStream(args[1]);
				} catch (IOException e) {
					e.printStackTrace();
				}
				fos.write(decoded);
				fos.close();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
	}

}