在 Delphi 中解压 DeflateStream (C#)

Decompress DeflateStream (C#) in Delphi

在我的应用程序中,我构建了一个 xml 结构并将其发送给 delphi 客户端。在 xml 的标签中,我有一个压缩的 base64 编码字符串:

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);
    }

我的 Delphi 客户端必须从该标签中获取数据并将其再次转换为基字符串。不幸的是,我得到了

ezdecompressionerror data error

我尝试了一些网络提供的功能,例如:

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;

但这里没有任何效果。我正在使用 XE2 和标准 Zlib 库。我阅读了一些文章,但我无法弄清楚:

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

我也试过用 c# 解压缩它,应该不会对它起作用感到惊讶。我想我的问题出在对delphi解压代码的理解上,或者我是一个真正的笨蛋。但不幸的是,我不明白我该如何完成这项工作。 :[

TIA

我将 re-write 两个代码块。我建议您使用 UTF-8 作为编码。对于大多数西方文本,它是最 space 高效的 Unicode 编码。

C# 代码如下所示:

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();
        }
    }
}

产生此输出:

H4sIAAAAAAAEAEsrTUosAgDmcA8FBQAAAA==

我基本上保留了您使用的相同代码,但切换到 UTF-8 并简化了代码,删除了一些不必要的步骤。我还删除了压缩缓冲区长度的写入。我认为没有必要,而且无论如何它都不遵守网络字节顺序。

更重要的是,我切换到 GZIP,因为在 Delphi 代码中更容易阅读。使用 deflate 会迫使你进入原始的 zlib 编程,这有点混乱。使用 GZIP 添加 GZIP header 到压缩流。

在 Delphi 端,代码如下所示:

{$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.

当然,对于小字符串,压缩开销和 GZIP header 意味着这不是压缩。加上 base64 编码,压缩+编码的字符串比输入长得多。

不过,我假设您希望发送大量文本,在这种情况下 GZIP header 将不重要。