0
votes

in my application i build a xml structure an send it to a delphi client. In a tag of that xml i have a zipped, base64 coded string:

public static string Zip(string text)
    {
        byte[] buffer = System.Text.Encoding.Unicode.GetBytes(text);
        MemoryStream ms = new MemoryStream();
        //using (System.IO.Compression.GZipStream zip = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Compress, true))
        //{
        //    zip.Write(buffer, 0, buffer.Length);
        //}

        using (System.IO.Compression.DeflateStream zip = new System.IO.Compression.DeflateStream(ms, System.IO.Compression.CompressionMode.Compress, true))
        {
            zip.Write(buffer, 0, buffer.Length);
        }

        ms.Position = 0;
        MemoryStream outStream = new MemoryStream();

        byte[] compressed = new byte[ms.Length];
        ms.Read(compressed, 0, compressed.Length);

        byte[] gzBuffer = new byte[compressed.Length + 4];
        System.Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length);
        System.Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4);
        return Convert.ToBase64String(gzBuffer);
    }

My Delphi client has to get the data from that tag and turn it into the basestring again. unfortunately, i get a

ezdecompressionerror data error

I tried some of the functions the internet provides, e.g.:

function ZDecompressString(aText: string): string;
  var
  Utf8Stream: TStringStream;
  Compressed: TMemoryStream;
  Base64Stream: TStringStream;
begin
  Base64Stream := TStringStream.Create(aText, TEncoding.ASCII);
  try
    Compressed := TMemoryStream.Create;
    try
      DecodeStream(Base64Stream, Compressed);
      Compressed.Position := 0;
      Utf8Stream := TStringStream.Create('', TEncoding.ANSI);
      try
        ZDecompressStream(Compressed, Utf8Stream);
        Result := Utf8Stream.DataString;
      finally
        Utf8Stream.Free;
      end;
    finally
      Compressed.Free;
    end;
  finally
    Base64Stream.Free;
  end;
end;

But nothing worked here. I am using XE2 and the standard Zlib library. I read through some articles but i cant figure something out:

http://forum.codecall.net/topic/76077-compress-and-decompress-with-zlib-library/

http://www.yanniel.info/2011/01/string-compress-decompress-delphi-zlib.html

Delphi XE and ZLib Problems

http://www.delphipraxis.net/89090-string-mit-gzip-ent-zippen.html

I also tried decompressing it in c# and should not suprise that it worked. I guess my problem lies at the udnerstanding of the delphi decompression code or maybe i am a real dumb person. But unfortunately i dont get it how i can make this work. :[

TIA

1
There's an unholy mess of encodings here. The C# code encodes the text as UTF-16LE. But then you declare a variable called Utf8Stream but use TEncoding.ANSI. Do you have a clear picture of the encodings that are in play?David Heffernan
@DavidHeffernan I tried it with other encodings, too. I didnt change the variable name for my testcases. Sorry for that.rmbl

1 Answers

3
votes

I'm going to re-write both blocks of code. I suggest that you use UTF-8 as your encoding. For most western text it is the most space efficient Unicode encoding.

The C# code looks like this:

using System;
using System.IO;
using System.IO.Compression;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        public static string Zip(string text)
        {
            byte[] utf8bytes = System.Text.Encoding.UTF8.GetBytes(text);
            MemoryStream compressedStream = new MemoryStream();
            using (var gzipStream = new GZipStream(compressedStream, 
                CompressionMode.Compress, true))
            {
                gzipStream.Write(utf8bytes, 0, utf8bytes.Length);
            }

            compressedStream.Position = 0;
            byte[] deflated = new byte[compressedStream.Length];
            compressedStream.Read(deflated, 0, (int)compressedStream.Length);
            return Convert.ToBase64String(deflated);
        }

        static void Main(string[] args)
        {
            Console.WriteLine(Zip("fubar"));
            Console.ReadLine();
        }
    }
}

Which produces this output:

H4sIAAAAAAAEAEsrTUosAgDmcA8FBQAAAA==

I've kept essentially the same code that you used, but switch to UTF-8 and streamlined the code removing some unnecessary steps. I've also removed the writing of the compressed buffer length. I don't see the need for that, and in any case it did not respect network byte order.

More importantly I switched to GZIP because it's easier to read that in Delphi code. Using deflate forces you into raw zlib programming which is a bit messy. Using GZIP adds a GZIP header to the compressed stream.

On the Delphi side the code looks like this:

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.Classes,
  System.ZLib,
  Soap.EncdDecd;

function Unzip(const zipped: string): string;
var
  DecompressionStream: TDecompressionStream;
  Compressed: TBytesStream;
  Decompressed: TStringStream;
begin
  Compressed := TBytesStream.Create(DecodeBase64(AnsiString(zipped)));
  try
    // window bits set to 15 + 16 for gzip
    DecompressionStream := TDecompressionStream.Create(Compressed, 15 + 16);
    try
      Decompressed := TStringStream.Create('', TEncoding.UTF8);
      try
        Decompressed.LoadFromStream(DecompressionStream);
        Result := Decompressed.DataString;
      finally
        Decompressed.Free;
      end;
    finally
      DecompressionStream.Free;
    end;
  finally
    Compressed.Free;
  end;
end;

procedure Main;
begin
  Writeln(Unzip('H4sIAAAAAAAEAEsrTUosAgDmcA8FBQAAAA=='));
end;

begin
  try
    Main;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

Of course, for small strings the compression overhead and the GZIP header means that this is not compression. Coupled with base64 encoding the compressed + encoded string is much longer than the input.

I'm presuming however, that you wish to send large amounts of text, in which case the GZIP header will not be significant.