简体   繁体   中英

Decompress DeflateStream (C#) in Delphi

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, eg:

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM