blog
blog copied to clipboard
Base64 encoding and decoding
Base64 is wonderful, at times. It can be great for serialization of small binary files, but not large ones. If you use base64 for serialization on binary files and then storing them as text into a database or sending that text in a web request - look into the BLOB type and HTTP POST with upload.
Using Embedded SQL, we are now able to encode and decode into base64 on the fly using SYSTOOLS.BASE64ENCODE
and SYSTOOLS.BASE64DECODE
. When you use these functions, the host variables you pass in should always be varying length (varchar
type). If you use a regular character type, it will also encode all the whitespace at the end of the variable.
Dcl-S VarcharField Varchar(256);
VarcharField = 'Hello WorksOfBarry blog!!';
EXEC SQL SET :VarcharField = SYSTOOLS.BASE64ENCODE(:VarcharField);
EXEC SQL SET :VarcharField = SYSTOOLS.BASE64DECODE(:VarcharField);
This will encode the VarcharField
variable, and then assign the encoding to it - also the same for decode. Of course, you can change where it assigns to and what it encodes.
CCSID Support
These two functions also support CCSIDs, including 1208 - which is used everywhere on the web, meaning it may prove useful for web services. Usually, when you encode it will look at the CCSID of the variable, and then the job (which comes off the system anyway). RPG gives the ability to change the CCSID of a variable using the CCSID
option when defining that variable. Notice below that I am using *UTF8
, but you can also use 1208
- or any CCSID you want. Check out the documentation for the CCSID keyword to see the available constants within this keyword.
Dcl-S UTF8Variable Varchar(100) Inz('') CCSID(*UTF8);
So in our example below, we have a normal varying length field which we want to convert to UTF-8 base64. To do that, we need a second variable with a CCSID of UTF-8. When we assign our regular variable to our variable with a certain CCSID, RPG will do the CCSID conversion for us - great! From there, we can simply use SYSTOOLS.BASE64ENCODE
against the UTF-8 field to get our UTF-8 base64 encoding.
Dcl-S SomeCharVar Varchar(50);
Dcl-S ToBeEncoded Varchar(100) Inz('') CCSID(1208);
SomeCharVar = 'hello db2 summit';
ToBeEncoded = SomeCharVar; //Convert it to 1208
//Encode based on 1208
EXEC SQL SET :ToBeEncoded = SYSTOOLS.BASE64ENCODE(:ToBeEncoded);
//By this point of the program, ToBeEncoded now contains UTF-8 base64.
That's it! You can out more at these links:
- BASE64ENCODE and BASE64DECODE on DB2 for z/OS - also works on DB2 for i.
- RPGLE CCSID keyword
Just tried the above example and it doesn't work:
Unable to retrieve query options file.
**** Starting optimizer debug message for query .
**** Ending debug message for query .
ODP created.
Blocking used for query.
User-defined function error on member QSQPTABL.
User-defined function error on member QSQPTABL.
ODP deleted.
Character conversion between CCSID 65535 and CCSID 1200 not valid.
Character conversion between CCSID 65535 and CCSID 1200 not valid.
Try this command before running the program with the conversion.
CHGJOB CCSID(37)
Character conversion between CCSID 65535 and CCSID 1200 not valid.
Try this command before running the program with the conversion.
CHGJOB CCSID(37)
I just came back to say that I needed to change my job CCSID, wish I'd have checked my emails 30 mins ago!! Thanks @mooska
Just to add to the above, base64decode doesn't like converting from 1208 even with job CCSID changed to 37 (as per the above workaround to get base64encode working)
running
DCL-S VarCharUTF VARCHAR(256) CCSID(*UTF8);
DCL-S VarCharUTF_enc VARCHAR(256) CCSID(*UTF8);
DCL-S VarCharUTF_dec VARCHAR(256) CCSID(*UTF8);
ChangeCCSID('37');
VarCharUTF = 'user:pass';`
EXEC SQL
SET :varcharutf_enc = systools.base64encode(:varcharutf);
// Returns dXNlcjpwYXNz
EXEC SQL
SET :varcharutf_dec = systools.base64decode(:varcharutf_enc);
// Error - Character conversion between CCSID 65535 and CCSID 1208 not valid
ChangeCCSID('*SYSVAL');
*inlr = *ON;
to work around this I had to do the following:
DCL-PR Hex EXTPROC('cvtch');
szTgt_short CHAR(32767) CCSID(*HEX) OPTIONS(*VARSIZE);
szSrc_long CHAR(65534) CONST OPTIONS(*VARSIZE);
nSrcLen INT(10) VALUE;
END-PR ;
DCL-S VarCharUTF VARCHAR(256) CCSID(*UTF8);
DCL-S VarCharUTF_enc VARCHAR(256) CCSID(*UTF8);
DCL-S varcharUTF_hexstring VARCHAR(512);
DCL-S varcharUTF_hex CHAR(256) CCSID(*HEX);
ChangeCCSID('37');
VarCharUTF = 'user:pass';`
EXEC SQL
SET :varcharutf_enc = systools.base64encode(:varcharutf);
// Returns dXNlcjpwYXNz
// Grab HEX output from Base64Decode
EXEC SQL
SET :varcharutf_hexstring =
HEX(systools.base64decode(:varcharutf_enc));
// Returns 757365723A70617373
Hex(VarCharUTF_hex :
%trim(varcharUTF_hexstring) :
%size(%trim(varcharUTF_hexstring)));
// eval VarCharUTF_hex :x returns 75736572 3A706173 73404040
VarCharUTF = %trim(VarCharUTF_hex);
// Returns user:pass
ChangeCCSID('*SYSVAL');
*inlr = *ON;
where ChangeCCSID just runs the change job command
If there is a better way around this, I would be glad to hear it!
NB: The reason I added the CCSID(*HEX) line to cvtch was because I was also trying this with CCSID(37) fields in the same program, to make it compatible with both methods I read into a Variable with CCSID(*HEX) then read into the relevant char field with CCSID(37) or CCSID(*UTF8)
I have retrieved a data string that is encoded in base64 and attempting to decode to binary to display as a document as a .png
Any assistance would be greatly appreciated.