Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Base64 and TByteDynArray - Solution

1,031 views
Skip to first unread message

G. Bradley MacDonald

unread,
Jul 20, 2001, 2:22:28 AM7/20/01
to
Hi Folks,

I have been having trouble trying to transfer a file between a Delphi
Client and a Java Web Service. We wanted to use Base64 encoding - but
there is a bug in D6 that doesn't allow the TByteDynArray to auto
encode/decode the Base64 stream. So - I couldn't transfer the file
without changing the code in the Java WS.

However - many thanks to Dan Miser who basically gave me the solution -
so I thought I would pass it on to other here. (Thanks again to Dan!)

When I import the WSDL in to my Delphi App - the Base64 return is shown
as a TByteDynArray. However, this won't work. But change the return
type to Variant (Manually) and then you can decode the Base64 stream
yourself. So below in the Interface I show the new and then the
original.

-------- INTERFACE GEN FROM WSDL --------
function get(const buzpak_handle: Int64): Variant; stdcall;
// function get(const buzpak_handle: Int64): TByteDynArray; stdcall;


Now in the Implementation I use a nice little component from Ralf Junker
<ralfj...@gmx.de> http://www.zeitungsjunge.de/delphi/ - and is now part
of the Jedi Code Library. This routine is very easy to use - and only
requires one line of code.

-------- IMPLEMENTATION --------
Var
TempStr :Variant;
TempStr2 :String;
FS :TFileStream;
SS :TStringStream;
begin
TempStr := (httprio1 as buzpakservice).Get(1);

TempStr2 := TempStr;

SS := TStringStream.Create('');
SS.WriteString(TempStr2);
FS := TFileStream.Create('c:\buzPAKs\aaaaTest2.buz', fmCreate);
FS.Position := 0;
SS.Position := 0;
MimeDecodeStream(ss, fs);

The result is a fully decoded file - stored in the FileStream - in other
words - written to file.


G. Bradley MacDonald

Shiv Kumar

unread,
Jul 20, 2001, 4:49:22 AM7/20/01
to
Bradley,

Thanks for sharing this information!

FYI - Indy has Base64 encode/decode routines and if I'm not mistaken (I
remember seeing the unit) Delphi 6 comes with its own Base64 encode/decode
routines as well.
--
Shiv Kumar
The Delphi Apostle
http://www.matlus.com
http://www.delphisoap.com


G. Bradley MacDonald

unread,
Jul 20, 2001, 9:57:58 AM7/20/01
to
In article <3b57ed7c_1@dnews>, sh...@matlus.com says...
Shiv,

You are correct about Indy - in fact Indy 8.1 (beta ?) has the ability to
encode/decode a Steam in place. Very nice - and you don't have to use
two!

Bradley

Shiv Kumar

unread,
Jul 20, 2001, 10:37:05 AM7/20/01
to
"G. Bradley MacDonald" <Bradley_...@telus.net> wrote in message
news:MPG.15c1d8c66...@newsgroups.borland.com...

> You are correct about Indy - in fact Indy 8.1 (beta ?) has the ability to
> encode/decode a Steam in place. Very nice - and you don't have to use
> two!

My kettle just spews it out <g> But then it's probably an older model than
yours.

G. Bradley MacDonald

unread,
Jul 20, 2001, 2:22:36 PM7/20/01
to
The Saga continues... <g>

As stated in my previous post I have a solution to receiving Base64
streams - but now I need to send one and I am getting a variant error
when I try.

The Java side uses the Base64 XML field to auto encode the stream. So I
have to encode and decode it manually right now (bug soon to be fixed in
D6). When I import the Web service I get a field type of TByteDynArray.
I have to change this to a Variant in order to be able to manually
decode/encode the stream. This works really well for incoming streams -
how ever it is failing on the outgoing. I keep getting an Invalid
Variant Operation when I try this.

Any thoughts off the top of your head? Am I doing something obviously
wrong?

Bradley

procedure TForm1.Button_SendBuzPAKClick(Sender: TObject);


Var
TempStr :Variant;
TempStr2 :String;
FS :TFileStream;
SS :TStringStream;

SendResult :Integer;
begin

// Get the file to transfer
If OpenDialog_Input.Execute Then Begin
// Create the two streams
FS := TFileStream.Create(OpenDialog_Input.FileName, fmOpenRead);
SS := TStringStream.Create('');
Try


FS.Position := 0;
SS.Position := 0;

// This Base64 Encodes the Stream and puts it in the String Stream
MimeEncodeStream(fs, ss);

SS.Position := 0;
// Now Read the Base64 String into a reg String
TempStr2 := SS.ReadString(SS.Size);
SS.Position := 0;
SS.Position := 0;

// Now here I am trying to create the Variant. I have tried just
// using a Variant without an array,
// I have also tried other variant types (ie varString) - but it
// fails as well.
TempStr := VarArrayCreate([0,(Length(TempStr2) -1)], varVariant);
TempStr := TempStr2;
// At this point in the code - I get a Variant Error - Invalid Variant
// Operation
SendResult := (httprio1 as buzpakservice).openFromData(TempStr);
ShowMessage('Send Result : ' + InttoStr(Sendresult));
Finally
FS.Free;
SS.Free;
End; { Try..Finally }
End; { If }
end;

Rosimildo daSilva

unread,
Jul 20, 2001, 2:53:35 PM7/20/01
to
G. Bradley MacDonald wrote in message ...
>I just noticed that on the call
> function openFromData(Const buzpak_data: Variant): Int64; stdcall;
>
>the variant is declared as a Const parameter. However, if I change this
>to not be a Const - then I get a Internal Server Error 500.
>
>Anyone have any ideas on this?


Do you have control of both ends ? ( server and client ), if so
you may try "hexBinary" encoding.

Does Delphi supports "xsd:hexBinary" ? Maybe you should try this type
of binary mapping.

Rosimildo.

G. Bradley MacDonald

unread,
Jul 20, 2001, 2:41:00 PM7/20/01
to
I just noticed that on the call
function openFromData(Const buzpak_data: Variant): Int64; stdcall;

the variant is declared as a Const parameter. However, if I change this
to not be a Const - then I get a Internal Server Error 500.

Anyone have any ideas on this?

Bradley

Rosimildo daSilva

unread,
Jul 20, 2001, 2:51:18 PM7/20/01
to
G. Bradley MacDonald wrote in message ...
>The Saga continues... <g>
>


WOW !, I feel your pain..... <g>

Rosimildo.


Rosimildo daSilva

unread,
Jul 20, 2001, 3:28:34 PM7/20/01
to
G. Bradley MacDonald wrote in message ...
>We have written both ends (Delphi and Java) - but we will go with
>PocketSoap for Delphi for our testing - and then maybe the final product.
>However, what we really want is to be able to use D6 - which means we
>would be upgrading 4 D5 Pro to 4 D6 Ent...

Sounds like a good plan....

>
>As well we have various other WebService clients that will have to access
>these services so doing special coding is out of the question -


Who talked about special coding. ?
I mentioned another XML Schema 2001 simple type, xsd:hexBinary
as another type to carry binary information. Most of the SOAP stacks
that I know of, supports it. I just asked if Delphi supports ? From the
application standpoint, it does not matter if you use base64 or
hexBinary does it ? ( as long as works !!! ).

>especially if we will be converting back after ward. So we have to use
>Base64
>


OK. looks like base64 is a requirement. <G>

Rosimildo.

G. Bradley MacDonald

unread,
Jul 20, 2001, 3:11:28 PM7/20/01
to
We have written both ends (Delphi and Java) - but we will go with
PocketSoap for Delphi for our testing - and then maybe the final product.
However, what we really want is to be able to use D6 - which means we
would be upgrading 4 D5 Pro to 4 D6 Ent...

As well we have various other WebService clients that will have to access

these services so doing special coding is out of the question -

especially if we will be converting back after ward. So we have to use
Base64

Thanks

Bradley

Noah Kriegel

unread,
Jul 20, 2001, 7:11:47 PM7/20/01
to
Well, I assume your first error occurred because variants can't be
consts, the second probably occured because your soap server (Java, right?)
was probably expecting an incoming paramater of type xs:base64Binary and I
doubt your variant was sent out in the correct format... 'though I guess I
don't really know.
Anyway, just because you had trouble bringing in a byte array and found
your way to the variant solution doesn't mean you can't kick out a byte
array, which will be sent as the base64Binary type. Just convert your
string and then set the length of a TByteDynArray to the length of the
converted string. Then, all you need to do is go through each character in
the string and stick the value of the byte which represent's it into the
appropriate cell of the array and, when that's all done, shoot out the array
as the result. Something like this:

type
PByte = ^Byte;
var
barray: TByteDynArray;
s: string;
p: pointer;
bp: PByte;
i: integer;
begin
// think of s as your streamed string...
s := 'Hello World';
// this function is in the idcoder3to4.pas file
s := Base64Encode(s);
SetLength(barray, StrLen(PChar(s)));
for i := 1 to StrLen(PChar(s)) do
begin
// use pointers to stick the actual byte value of the character into the
array...
p := @s[i];
bp := PByte(p);
barray[i-1] := bp^;
end;
// kick it back out...
result := barray;
end;

Does this help? Did I understand your problem correctly? Let me know, I'd
be interested to see how it turns out.

"G. Bradley MacDonald" <Bradley_...@telus.net> wrote in message

news:MPG.15c21b18d...@newsgroups.borland.com...

G. Bradley MacDonald

unread,
Jul 21, 2001, 1:32:15 AM7/21/01
to
Noah,

I will not be able to test this until Monday. But I will let you know
ASAP. Thank you for the reply!

Bradley

Frits Regtien

unread,
Jul 23, 2001, 6:44:50 AM7/23/01
to

Hi Bradley,

I'm trying to do the same and tried your code. I also got the Internal Server Error 500. I noticed that I got this with file that were a bit longer...

I used the following code to monitor what was happening:

procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
idsmtp.QuickSend('smtp.comp.nl', 'test','m...@comp.nl',
'm...@comp.nl', request.content);
end;

This sends me an Email with the SOAP document. A part looks like this (just an example):
<NS1:filecontent xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="xsd:unsignedByte[477]"><NS1:item>91</NS1:item><NS1:item>98</NS1:item><NS1:item>13</NS1:item></NS1:filecontent>

Every byte in the array is translated into something like <NS1:item>98</NS1:item>. So at least 23 bytes! This is not what we want, is it? We want it to be Base64 encoded.

In case you send a bit longer file the line seems to be truncated, hence the Internal Server Error 500.

I haven't found the solution yet, but I hope this helps. Keep us posted...

Frits.

G. Bradley MacDonald

unread,
Jul 23, 2001, 11:42:12 AM7/23/01
to
Thanks for the post.

Borland is aware of the issue with the TByteDynArray - and is working on
a fix. However, with the conference going on - I think they might be a
little distracted <g> and will probably not have a fix until after.

Bradley

PS. If you need a work around - you can get PocketSoap and use it until
the fix for D6 comes out.

G. Bradley MacDonald

unread,
Jul 23, 2001, 11:45:13 AM7/23/01
to
Hello Noah,

Thanks for the post - but the problem with using a TByteDynArray is that
it is not converted properly. Each byte is converted and surrounded in
<ns1> tags - so the Java server doesn't know what to do with it.

I was hoping that I would be able to fudge it with the variant - but I
think I am going to have to wait for Borland's fix :(

Bradley

G. Bradley MacDonald

unread,
Jul 24, 2001, 12:27:19 PM7/24/01
to
Noah,

Wow! Great stuff. This class seems to be sending things across the wire
correctly - but our Java server is still dieing on the Send of the
Base64. So I can receive the Base64 - but not send it to the server.

However, the Server guys are starting to think that it may be something
on their side <g> So - I will keep you posted as to what we discover
today!

Thank you for the assistance

Bradley

In article <3b5cec96_2@dnews>, nkri...@thedswgroup.com says...
> I see what you mean, however, you got me thinking and I think I have a
> solution for you

Rosimildo da Silva

unread,
Jul 25, 2001, 5:21:33 PM7/25/01
to
"G. Bradley MacDonald" <Bradley_...@telus.net> wrote in message
news:MPG.15c8d1093...@newsgroups.borland.com...
> Hello Noah,
>
> The saga continues... <g> It looks like it may be an interop issue.
>
> The real issue - as far a I can see is that we are having an issue with
> how Delphi is setting up the soap packet. The error I am now getting is
> shown below in the error section. It is basically saying what we
> suspected is that the delphi stream being sent is using a different
> encoder than what the Java side seems to want to use - according to the
> Soap Envelope.
>
> For example the ItWorks section below shows a snipet of a Soap Envelope
> we have been able to use to make it work with PocketSoap. We basically
> force just this packet to be sent. Notice the declaration of 'NS2' -
> xmlns:ns2="http://schemas.xmlsoap.org/soap/encoding/". Now take a look
> at the D6 Section and notice the declaration of the xds name space
> xmlns:xsd="http://www.w3.org/1999/XMLSchema".
>


Your analysis is correct. From the Envelopes below, this should not work.

The SOAP-ENC namespace "http://schemas.xmlsoap.org/soap/encoding/"
defines a type called "base64", using this namespace should work.

The XML Schema 2001 changed the name of the "base64" to "base64Binary".
So, xmlns:xsd="http://www.w3.org/2001/XMLSchema" for the "xsd" namepsace,
the type used should be "xsd:base64Binary", otherwise it is an invalid type
for the given namespace.

Another thing to watch for is the XML Schema used. It looks like that you
java server only accepts "2001" XML schema. If this is true, it most likely
would not know what to do with "1999" schema.

Rosimildo.


G. Bradley MacDonald

unread,
Jul 25, 2001, 4:50:49 PM7/25/01
to
Hello Noah,

The saga continues... <g> It looks like it may be an interop issue.

The real issue - as far a I can see is that we are having an issue with
how Delphi is setting up the soap packet. The error I am now getting is
shown below in the error section. It is basically saying what we
suspected is that the delphi stream being sent is using a different
encoder than what the Java side seems to want to use - according to the
Soap Envelope.

For example the ItWorks section below shows a snipet of a Soap Envelope
we have been able to use to make it work with PocketSoap. We basically
force just this packet to be sent. Notice the declaration of 'NS2' -
xmlns:ns2="http://schemas.xmlsoap.org/soap/encoding/". Now take a look
at the D6 Section and notice the declaration of the xds name space
xmlns:xsd="http://www.w3.org/1999/XMLSchema".

So (bare with my lack of knowledge here) it seems that Delphi is saying
that is using 'X' to encode something - and then the Java side is saying
that it can't find X in the name space it thinks it should be using.

Note that there is another call to a function to get a simple list before
the Base64 call in the dumpfile.

Also note that the two calls in the samples below are different - but are
attempting to do the same basic thing - send a base64 file across.

Bradley MacDonald


---------------------------------
Error Section
---------------------------------
<SOAP-ENV:Envelope xmlns:SOAP-
ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Client</faultcode>
<faultstring>No Deserializer found to deserialize a
'http://www.w3.org/1999/XMLSchema:base64' using encoding style
'http://schemas.xmlsoap.org/soap/encoding/'.</faultstring>
<faultactor>/soap/servlet/rpcrouter</faultactor>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
---------------------------------


---------------------------------
It Works Section
---------------------------------
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<ns1:openFromData xmlns:ns1="urn:buzpakservice"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<buzpak-data xmlns:ns2="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="ns2:base64">
-----------BASE 64 ENCODED FILE HERE------
</buzpak-data>
</ns1:openFromData>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
---------------------------------


---------------------------------
D6 Section
---------------------------------
<?xml version="1.0" encoding='UTF-8'?><SOAP-ENV:Envelope xmlns:SOAP-
ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/1999/XMLSchema"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:SOAP-
ENC="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-
ENV:Body><NS1:requestActivation xmlns:NS1="urn:activationservice-service"
SOAP-
ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><NS1:userid
xsi:type="xsd:string">kent</NS1:userid><NS1:password
xsi:type="xsd:string">kentpw</NS1:password><NS1:buzPackName
xsi:type="xsd:string">aaabDecoded2.buz</NS1:buzPackName><NS1:buzPackData
xsi:type="xsd:base64">UEsDBBQAAgAIAEBt6ir7egNPYwMAAEcLAAALAAAAV2ViRGVzay5
YT
---------------------------------

G. Bradley MacDonald

unread,
Jul 26, 2001, 11:55:41 AM7/26/01
to
Rosimildo,

Thanks for the reply! And the information.

Noah Kriegel sent me a new version of his Base64 Remotable class that
works GREAT!!!

This new class allows me to send and receive BASE 64 Binary files to/from
our Java server.

Bradley

0 new messages