Reputation: 50
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
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
Upvotes: 0
Views: 2477
Reputation: 612794
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.
Upvotes: 4