Von APD über COM nach SOAP Teil 4

Datenaustausch von BLOBS

Die Beispielfunktion „ContactSave(..)“ aus Teil 3 der Serie wird um einen Parameter für BLOBs (binary large objects) bzw. ein byte[]-Array erweitert. Damit wird es z.B. möglich, Dateien in der Datenbank zu speichern. In unserem Fall handelt es sich um Bilddaten.

Zunächst scheint der Team-Developer dafür keinen geeigneten Datentyp für die COM-Schnittstelle zur Verfügung zu stellen – es gibt ja nur Number, Boolean, DateTime und String. Zwar wäre eine Übergabe der Bilddaten in einem String denkbar, scheitert aber, sobald der Bytewert 0 in den Daten auftaucht (Gupta kann damit umgehen – nicht jedoch die COM-Schnittstelle). Für diesen Fall muss man die von Gupta bereitgestellte „Automation.apl“ einbinden. Diese befindet sich entweder direkt im Hauptverzeichnis oder im Unterverzeichnis \AxLibs. Und schon haben wir unter anderen die Klasse „Variant“ zur Verfügung, die in der COM-Schnittstelle auch verwendet werden kann. Diese korrespondiert z.B. in C# mit der Klasse „Object“.

Die Funktion ContactSave(..) wird um den Parameter

 Variant: vImage

erweitert.  Da dieser Parameter alle möglichen Datentypen enthalten kann, sollten wir zunächst prüfen, ob der übergebene Variant das erwartete byte[]-Array enthält. Alternativ darf er auch NULL sein. Der Typ des im Variant enthaltenen Datenpakets lässt sich mit Hilfe der Memberfunktion ActiveXType() ermitteln. In unserem Fall erwarten wir ein Array of Bytes, also eine Kombination aus VT_Array und VT_I1. Leider ist die in der Automation.apl hinterlegte Liste der VT_xxx-Konstanten nicht vollständig. VT_Array fehlt dort. Man kann die Konstanten jedoch z.B. in der Include-Datei WTypes.h von Microsoft finden – oder auch hier. Wir prüfen den Variant auf OBJ_Null und falls das nicht der Fall ist, dessen ActiveX-Type auf VT_Array und VT_I1:

! Check Variant for proper type and if so extract byte[] to a Gupta-Long String ( lsImage )
If (vImage = OBJ_Null) 
 ! we accept that 
Else If ( (vImage.ActiveXType() & VT_ARRAY) AND (vImage.ActiveXType() & VT_I1) )
 If ( NOT vImage.GetBlob( lsImage ) )
  Set rsErrorMsg = 'Could not extract byte[]'
  Return FALSE
Else
 Set rsErrorMsg = 'Parameter vImage has wrong type. Must be byte[].'
 Return FALSE

Als nächstes muss nur noch das Insert-Statement etwas angepasst werden. Da der Long String lsImage Binärdaten enthält, muss man per SqlSetLongBindVar(..) diesen vorbereiten:

! insert data
Set sSql = 'INSERT INTO GuptaComContact ( ContactId, FirstName, LastName, Birthday, LuckyNumber, Image )
 VALUES ( :nContactId, :sFirstName, :sLastName, :dtBirthday, :nLuckyNumber, :lsImage)'
Set bOk = SqlPrepare( hSql, sSql ) 
If ( bOk )
 Call SqlSetLongBindDatatype( 6, 23 )
 Call SqlExecute( hSql )
 Call SqlPrepareAndExecute( hSql, 'COMMIT' )

Damit ist das COM-Objekt schon fertig. Nur noch bauen und registrieren.

Der Webservice

Wechseln wir zu unserem Webservice-Projekt im Visual Web-Developer. Da wir die COM-Schnittstelle verändert haben (der Parameter vImage ist hinzu gekommen) muss der Verweis in unserem Projekt aktualisiert werden. Dazu muss man diesen zunächst löschen und erneut hinzufügen.

Der WebService ContactSave(..) wird um den Parameter sImageFile (Pfad und Dateiname, der auf ein Image-File zeigt) erweitert. Man könnte natürlich auch ein byte[]-Array stattdessen übergeben, aber dann lässt sich der Webservice nicht so schön testen…

 Object oImage = null; //used as Parameter in cGc.ContactSave(..)
 Image imgFile = null;
 // load Image and transform to Byte-Array
 if (sImageFile.Length > 0)
 {
  try
  {
   imgFile = Image.FromFile(sImageFile);
   MemoryStream streamImage = new MemoryStream();
   imgFile.Save(streamImage, System.Drawing.Imaging.ImageFormat.Jpeg);
   oImage = streamImage.GetBuffer();
  }
  catch (Exception ex)
  {
   cResult.bRet = false;
   cResult.sErrorMsg = ex.Message;
   return cResult;
   }
 }

Ist eine Datei angegeben, wird versucht, diese in eine Image-Klasse zu laden. Damit haben wir zwei Fliegen mit einer Klappe erschlagen: Wir stellen sicher, dass die Datei existiert und es sich um eine valide Bilddatei handelt. Um an die Bilddaten im byte[]-Format zu kommen, kann man das Bild in einen Memory-Stream speichern, wobei man die Möglichkeit bekommt, es in ein bestimmtes Format (hier Jpeg) zu wandeln. Das ist sehr nützlich, da Gupta nicht ganz so viele Bildformate versteht (z.B. kein .png). Und wenn man das Bild innerhalb des Gupta Team Developers weiter verwenden will, hat man nun sichergestellt, dass es dort als Jpeg verwendbar ist.

Jetzt muss nur noch der COM-Aufruf erweitert werden, dann sind wir fertig:

cResult.bRet = cGc.ContactSave(sFirstName, sLastName,
dtBirthday, nLuckyNumber, oImage, ref cResult.sErrorMsg);

Wenn wir nun F5 drücken, können wir den WebService dank der Automatismen in Visual Studio direkt ausführen und testen. Das wäre mit einem byte[]-Parameter nicht möglich. In diesem Fall müsste man sich ein komplett eigenes Programm schreiben, das den Webservice aufruft. Aber das machen wir erst im nächsten Teil…

Hier die Files:

GuptaCom.app

GuptaSoap.zip

Advertisements

Über thomasuttendorfer
Ich bin Entwicklungsleiter bei der Softwarefirma [ frevel & fey ] in München. Wir entwickeln Business-Software für Verlage und verwenden dafür den Gupta Team-Developer sowie Visual Studio.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: