-
BLOB mit BASE64 encoden für REST Service im IBM i IWS
Hallo zusammen
Ich habe die Anforderung aus einem BLOB Feld einer AS400 Tabelle, welches ein Programmobjekt für eine Android App beinhält (ca. 5 MB), mittels REST Service über den IBM i integrated web services server, dieses als BASE64 String zur Verfügung zu stellen.
Grundsätzlich dachte ich, das mit folgendem SQL zu machen, aber das funktioniert leider nicht, da der BLOB Inhalt zu groß ist. SYSTOOLS.BASE64ENCODE ist auf 2732 Zeichen beschränkt.
Code:
Select cast(systools.base64encode(Datei) AS clob ccsid 1208) as Datei from Versionsverwaltung
where programm = 'StaplerTerminal.apk' and versionsnr = '1.0.5';
Daher war dann mein Ansatz, das BLOB Feld in einem CLOB (CCSID 1208) als BASE64 zu speichern. Dies wollte ich mittels apr_base64_encode_binary bewerkstelligen. Die Funktion habe ich aus einem Foreneintrag übernommen. Nur komme ich auf keinen grünen Zweig, da ich, egal was ich anstelle, immer falsche BASE64 Daten erzeuge.
Code:
dcl-s Blob sqltype(BLOB:16773100);
dcl-s Clob sqltype(CLOB:16773100) CCSID(1208);
dcl-s DateiClob sqltype(CLOB:16773100) CCSID(1208);
dcl-s ClobOut sqltype(CLOB:16773100) CCSID(1208);
dcl-s LocString like(Clob);
dcl-s LocBase64 like(Clob);
exec sql
select datei into :Blob,
from Versionsverwaltung
where programm = trim(:Programm) and versionsnr = trim(:Version)
limit 1;
if sqlcode = 0;
ClobOut = getBase64(Blob);
ClobOut_Len = %len(%trimr(ClobOut));
exec sql
update Versionsverwaltung set DateiClob = :ClobOut
where aktuell='1' and
where programm = trim(:Programm) and versionsnr = trim(:Version);
endif;
*inlr = *on;
return;
// _______________________________________________________________
// getBASE64 Encoded String ____________________________________
dcl-proc getBase64;
dcl-pi *n like(Clob) rtnparm; // Encoded String
PiString like(Blob);
end-pi;
dcl-s LocString like(Blob) inz; // Source String
dcl-s LocBase64 like(Clob) inz; // Encoded String
dcl-s Loclength int(10); // StringLength
dcl-s LocEnclen int(10); // EncodedLength
LocString = %trimr(PiString);
LocLength = %len(%trimr(LocString)); // String-Length
LocEnclen = encbase64bin(%addr(LocBase64): // Encode Base64
%addr(LocString):LocLength);
if LocEnclen > *zero; // Encoded Length
return %subst(LocBase64:1:LocEnclen - 1);// without last null
else;
return *blanks;
endif;
end-proc;
Hat jemand eine Idee, was ich hier falsch mache oder ist meine Herangehensweise falsch? Bin für jeden Tipp dankbar.
Grüße, Ingo
-
Von Scott Clement gibt es ein "Base64 Encode/Decode for ILE RPG":
https://www.scottklement.com/base64/
Falls Du ein Beispiel brauchst, müsste ich eins raussuchen...
(nedoisiswuascht ;- )
-
Dies könnte auch mit deinem Basiscode der Quelle zusammenhängen.
Du speicherst das zwar als Blob, aber ist es auch Codepage 1252 oder UTF8?
Vielleicht ist es ja auch EBCDIC (273, o.ä.), somit kommt beim Decode auch nur EBCDIC wieder raus.
-
Da geb ich dir grundsätzlich recht, was die Codepages betrifft. Mich verwirrt nur, dass das SQL (getestet mit kleinem Programmobjekt) richtig codierte Daten zurückliefert.
Select cast(systools.base64encode(Datei) AS clob ccsid 1208) as Datei from Versionsverwaltung
where programm = 'StaplerTerminal.apk' and versionsnr = '1.0.5';
-
Das ist schon korrekt wenn du auf dem selben System codierst und decodierst.
Da du BAS64 aber wohl mit Nicht-IBMi austauschen willst wollen die wohl beim dekodieren keinen EBCDIC-Code.
Du kodierst das A in 273 und dekodierst dies in UTF8 (1252) = Á
Die andere Frage ist, nimmt Scott auch dieselbe Base64-Zeichenfolge wie die Windows-Welt?
https://de.wikipedia.org/wiki/Base64
-
Hallo Ingo,
die Anforderung ist nicht so ganz einfach, weil man hier leicht an die 16MB Grenze im RPG stößt. Deine Idee ist schon richtig mit der API apr_base64_encode_binary, denn die kann auch über Pointer anprogrammiert werden und da ist die Grenze viel größer. Ich habe eine Lösung gebaut, die für CLOB Daten funktioniert. BLOB geht genauso. Es ist zu beachten, dass die API die Daten im UTF8 Format = CCSID(*UTF8) verlangt.
Damit es mit dem Compilieren einfacher ist, habe ich ein CL gebaut, das ein BNDDIR für die BASE64 API's erstellt
Code:
/*********************************************************************/
/* */
/* CREATE BASUTL BINDING-DIRECTORY - BASE64 */
/* */
/****************** */
/* R.ROSS 03.2017 * */
/*********************************************************************/
PGM
DCL VAR(&LIB) TYPE(*CHAR) LEN(10) VALUE(DEVO)
MONMSG MSGID(CPF0000)
/*********************************************************************/
/* VERARBEITUNG */
/*********************************************************************/
DLTBNDDIR BNDDIR(&LIB/BASUTL)
CRTBNDDIR BNDDIR(&LIB/BASUTL) TEXT('BASE64 Utilities')
ADDBNDDIRE BNDDIR(&LIB/BASUTL) OBJ((QSYSDIR/QAXIS10HT *SRVPGM))
/*********************************************************************/
/* ENDE */
/*********************************************************************/
ENDE: ENDPGM
/*********************************************************************/
Anbei das RPG für das Erzeugen von BASE64-Daten aus CLOB oder BLOB
Code:
ctl-opt main(main) alloc(*teraspace) bnddir('BASUTL');
//------------------------------------------------------------------//
// //
// Encode BASE64 with Pointer //
// //
//----------------- //
// R.Ross 04.2020 * //
//------------------------------------------------------------------//
// BASE64 - Encode String - SRVPGM(QSYSDIR/QAXIS10HT) //
//------------------------------------------------------------------//
dcl-pr encBase64Bin int(10) extproc('apr_base64_encode_binary');
PiEnc_p pointer value; // Encoded-Pointer
PiSrc_p pointer value; // Source-Pointer
PiSrclen int(10) value; // Source-Length
end-pr;
//------------------------------------------------------------------//
// MemCopy //
//------------------------------------------------------------------//
dcl-pr memcpy extproc(*dclcase);
mctarget pointer value;
mcsource pointer value;
mclength uns(10) value;
end-pr;
//------------------------------------------------------------------//
// Array BASE64 Ergebnis //
//------------------------------------------------------------------//
dcl-ds DsBase64 qualified;
String_p pointer;
Length int(10);
end-ds;
//------------------------------------------------------------------//
// Main //
//------------------------------------------------------------------//
dcl-proc Main;
dcl-s LocClob SQLType(CLOB:5000000) ccsid(*utf8); // 5 MB
dcl-s LocClob64 SQLType(CLOB:5000000) ccsid(*utf8); // 5 MB
exec sql set :LocClob = 'test';
// Daten in BASE64 codieren
DsBase64 = encodeBase64(%addr(LocClob_data):LocClob_len);
// BASE64-Daten aus Pointer in CLOB LocClob64 laden - test -> dGVzdA==
memcpy(%addr(LocClob64_data):DsBase64.String_p:DsBase64.Length);
LocClob64_len = DsBase64.Length;
end-proc;
//------------------------------------------------------------------//
// Encode Data to BASE64 //
//------------------------------------------------------------------//
dcl-proc encodeBase64;
dcl-pi *n likeds(DsBase64); // Encoded Data
PiString_p pointer const; // String Pointer
PiLength int(10) const; // String Length
end-pi;
dcl-ds PsBase64 likeds(DsBase64) inz;
dcl-s LocEnclen int(10); // Encoded Length
if PiLength < 50;
PsBase64.String_p = %alloc(PiLength * 10); // alloc Pointer
else;
PsBase64.String_p = %alloc(PiLength * 5); // alloc Pointer
endif;
LocEnclen =
encBase64Bin(PsBase64.String_p:PiString_p:PiLength);
if LocEnclen > *zero; // Encoded Length
PsBase64.Length = LocEnclen - 1; // without last null
endif;
return PsBase64;
end-proc;
//------------------------------------------------------------------//
Bei Fragen kannst Du dich gern an mich wenden
Herzliche Grüße
Rainer
-
Hallo Ingo,
noch ein Tipp. Bei einem CLOB-Feld wie diesem
dcl-s Clob sqltype(CLOB:16773100) CCSID(1208);
erzeugt der Compiler eine Struktur mit 2 Feldern
CLOB_Data für die Daten und
CLOB_LEN für die Länge der Daten
In diesem Fall musst Du die Adresse der Daten nicht mit %Addr(CLOB)
sondern mit %Addr(CLOB_Data) an die Prozedur übergeben.
@Forum - wenn es eine einfachere Lösung gibt, dann lasst es mich wissen.
Herzliche Grüße
Rainer
-
Hallo Rainer
Ja, das war's, danke gleich mal. Hab mit CLOB anstelle von CLOB_Data das Eincoding gemacht. Steht dann auch der richtig codierte Inhalt in der RPG Clob Variablen. Aber eine Frage hab ich noch, wenn ich nun die CLOB Spalte (utv-8) in der Tabelle mittels SQL mit dieser Hostvariablen aktualisiere steht da Schrott drinnen. Muss ich da noch irgendwo konvertieren?
Soll BASE64: UEsDBBQAAAAIACEIIQLtOXirrAE
Ist BASE64: 䅢Ă˜AAɁÅɉؓAŒŁA偁A觕
lg, Ingo
-
Wie schaust du dir den Inhalt an?
Base64 beibt ASCII-Code!
Im Debugger kannst du das nur Hex kontrollieren.
Wenn du den Base64-Wert per SQL in eine UTF-8 -Spalte schreibst kannst du ihn per SQL in 273 wieder ansehen.
Du kannst den Inhalt auch temporär in eine Char/UCS2-Variable schieben. Die Umwandlung erfolgt dann auch automatisch.
-
Im Debug sehe ich den richtigen Inhalt
Code:
EVAL locClob64 LOCCLOB64_LEN OF LOCCLOB64 = 7206264
LOCCLOB64_DATA OF LOCCLOB64 =
....5...10...15...20...25...30...35...40...45...50...55...60
1 'UEsDBBQAAAAIACEIIQLtOXirrAEAAEkEAAAVAAAAYXNzZXRzL0VNREtDb25m'
61 'aWcueG1sjVNLb5tAEL5Xyn+Ycq3AYKdNZGFHkZ1WVUVVyaSHXqzJMuBVYNda'
121 '1k7Jr+/gxyYo0FbitDPfY74Z4pvfVQl7MrXUauZFQegBKaEzqYqZd59+9q+9'
181 'm3n83vfTjayBP1SAO6uhIEUGLWWQabGrSNkAFhtUBdXAZdu2nytQYQMCdzWB'
Variablendefinition: dcl-s LocClob64 sqltype(CLOB:16773100) CCSID(*UTF8);
Jobdefinitionsattribute:
Code:
Sprachen-ID . . . . . . . . . . . . . . . . . . . : DEU Landes- oder Regions-ID . . . . . . . . . . . . . : AT
ID des codierten Zeichensatzes (CCSID) . . . . . : 65535
Standard-ID des codierten Zeichensatzes . . . . . : 273
Steuerung für Zeichen-ID . . . . . . . . . . . . : *DEVD
Die Abfrage und das Ergebnis der Daten über Webservice IWS sieht dann so aus:
lg, Ingo
-
Hallo Ingo,
soll der Webservice ungefähr so aussehen?
http://www.myhofi.com/myapp/webbase64.pgm?data=test
in diesem Fall wandelt er den String "test" richtig in "dGVzdA==" um
-
Hallo Rainer
Ja, genau so sollte er aussehen, nur bei mir kommt immer folgendes raus:
{ "DATEI" : "䅢Ă˜AAɁÅɉؓAŒŁA偁A觕ՙţĂ惤??œ??N胘??ƈ 夥威∧ՄљNa??????鴗??礁????蘩??...
lg, Ingo
Similar Threads
-
By ismiavoiwuascht in forum NEWSboard Programmierung
Antworten: 0
Letzter Beitrag: 25-04-20, 10:18
-
By mk in forum IBM i Hauptforum
Antworten: 3
Letzter Beitrag: 19-06-19, 07:26
-
By Flappes in forum NEWSboard Programmierung
Antworten: 2
Letzter Beitrag: 22-05-17, 11:22
-
By dschroeder in forum IBM i Hauptforum
Antworten: 14
Letzter Beitrag: 31-08-16, 15:32
-
By dschroeder in forum NEWSboard Programmierung
Antworten: 5
Letzter Beitrag: 01-10-14, 10:52
Tags for this Thread
Berechtigungen
- Neue Themen erstellen: Nein
- Themen beantworten: Nein
- You may not post attachments
- You may not edit your posts
-
Foren-Regeln
|
Erweiterte Foren Suche
Google Foren Suche
Forum & Artikel Update eMail
AS/400 / IBM i
Server Expert Gruppen
Unternehmens IT
|
Kategorien online Artikel
- Big Data, Analytics, BI, MIS
- Cloud, Social Media, Devices
- DMS, Archivierung, Druck
- ERP + Add-ons, Business Software
- Hochverfügbarkeit
- Human Resources, Personal
- IBM Announcements
- IT-Karikaturen
- Leitartikel
- Load`n`go
- Messen, Veranstaltungen
- NEWSolutions Dossiers
- Programmierung
- Security
- Software Development + Change Mgmt.
- Solutions & Provider
- Speicher – Storage
- Strategische Berichte
- Systemmanagement
- Tools, Hot-Tips
Auf dem Laufenden bleiben
|
Bookmarks