使用zlib压缩Base64字符串

时间:2017-01-20 11:23:17

标签: delphi base64 zlib delphi-10.1-berlin

我需要通过TCP协议从Windows发送到移动设备,iOS和Android,这是一个很大的Base64字符串 我没有问题发送和接收,但字符串大小太大,大约24000个字符,我正在寻找压缩解压缩这些字符串的方法。
我看,最好的方法是使用Zlib,我发现这些链接Delphi XE and ZLib Problems (II)解释了如何操作。
这些函数适用于普通文本字符串,但压缩base64字符串会使它们更大 我将发送的一个非常小的字符串的例子是:

cEJNYkpCSThLVEh6QjNFWC9wSGhXQ3lHWUlBcGNURS83TFdDNVUwUURxRnJvZlRVUWd4WEFWcFJBNUZSSE9JRXlsaWgzcEJvTGo5anQwTlEyd1pBTEtVQVlPbXdkKzJ6N3J5ZUd4SmU2bDNBWjFEd3lVZmZTR1FwNXRqWTVFOFd2SHRwakhDOU9JUEZRM00wMWhnU0p3MWxxNFRVdmdEU2pwekhwV2thS0JFNG9WYXRDUHhTdnp4blU5Vis2ZzJQYnRIdllubzhKSFhZeUlpckNtTGtUZHVHOTFncHVUWC9FSTdOK3JEUDBOVzlaTngrcEdxcXhpRWJ1ZXNUMmdxOXpJa0ZEak1ORHBFenFVSTlCdytHTy ==

我不知道压缩这种类型的字符串是否可行。我需要帮助 我使用的功能是:

uses
  SysUtils, Classes, ZLib, EncdDecd;

function CompressAndEncodeString(const Str: string): string;
var
  Utf8Stream: TStringStream;
  Compressed: TMemoryStream;
  Base64Stream: TStringStream;
begin
  Utf8Stream := TStringStream.Create(Str, TEncoding.UTF8);
  try
    Compressed := TMemoryStream.Create;
    try
      ZCompressStream(Utf8Stream, Compressed);
      Compressed.Position := 0;
      Base64Stream := TStringStream.Create('', TEncoding.ASCII);
      try
        EncodeStream(Compressed, Base64Stream);
        Result := Base64Stream.DataString;
      finally
        Base64Stream.Free;
      end;
    finally
      Compressed.Free;
    end;
  finally
    Utf8Stream.Free;
  end;
end;

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

2 个答案:

答案 0 :(得分:3)

据我所知,您已完成以下问题:

  1. 将字符串编码为UTF-8字节。
  2. 使用zlib压缩这些字节。
  3. Base64编码压缩字节。
  4. 然后尝试压缩步骤3的输出并发现结果不小。这是可以预料的。您已经压缩了数据,并且不能期望进一步尝试压缩数据显着减小大小,特别是如果您同时使用base64编码则不会。如果你可以反复压缩数据并且每次都变得更小,那么最终就没有任何东西了。这显然是不可能的。

    我认为你已经做得很好。您转换为UTF-8,对于大多数文本,它是Unicode编码中空间效率最高的。如果你使用中文文本,那么你最好使用UTF-16。然后压缩UTF-8,这也是合理的。最后,对于传输,您使用base64编码,也是合理的。

    减少要传输的数据大小的最明显方法是省略base64步骤。如果您可以传输在步骤2中生成的压缩字节,那么您将减少传输。 Base64使用4个字节来编码3个字节,因此base64编码数据的大小比输入数据大三分之一。

    另一种方法可能是使用比zlib更好的压缩算法,但同样可以实现的限制。通常以增加计算时间为代价实现更好的压缩。

答案 1 :(得分:0)

@DavidHeffernan,我想我理解你了。
我创建了一个示例项目,我认为这是我必须要做的。你有什么看法?

unit UFrmMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm2 = class(TForm)
    Edit1: TEdit;  //Password Text
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Button1: TButton;
    Button2: TButton;
    Memo1: TMemo; //Original Text
    Memo2: TMemo;  //Result Text
    Label4: TLabel;
    Label5: TLabel;
    procedure Button1Click(Sender: TObject); //encrypt Button
    procedure Button2Click(Sender: TObject); //decrypt Button
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    function EncriptarAES(Dato, Password: String): String;
    function DesencriptarAES(Dato, Password: String): String;
    function CompressAndEncodeString(const Str: string): string;
    function DecodeAndDecompressString(const Str: string): string;
    procedure OnIdle(Sender: TObject; var ADone: Boolean);
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

uses TntLXCryptoUtil, uTPLb_Codec,
  uTPLb_BaseNonVisualComponent, uTPLb_CryptographicLibrary, ZLib, EncdDecd;

procedure TForm2.Button1Click(Sender: TObject);
begin
  Memo2.Lines.Clear;
  Memo2.Text := EncriptarAES(Memo1.Text, Edit1.Text);
end;

procedure TForm2.Button2Click(Sender: TObject);
begin
  Memo1.Lines.Clear;
  Memo1.Text := DesencriptarAES(Memo2.Text, Edit1.Text);
end;

function TForm2.CompressAndEncodeString(const Str: string): string;
var
  Utf8Stream: TStringStream;
  Compressed: TMemoryStream;
  Base64Stream: TStringStream;
begin
  Utf8Stream := TStringStream.Create(Str, TEncoding.UTF8);
  try
    Compressed := TMemoryStream.Create;
    try
      ZCompressStream(Utf8Stream, Compressed, TZCompressionLevel.zcMax);
      Compressed.Position := 0;
      Base64Stream := TStringStream.Create('', TEncoding.ASCII);
      try
        EncodeStream(Compressed, Base64Stream);
        Result := Base64Stream.DataString;
      finally
        Base64Stream.Free;
      end;
    finally
      Compressed.Free;
    end;
  finally
    Utf8Stream.Free;
  end;
end;

function TForm2.DecodeAndDecompressString(const Str: string): string;
var
  Utf8Stream: TStringStream;
  Compressed: TMemoryStream;
  Base64Stream: TStringStream;
begin
  Base64Stream := TStringStream.Create(Str, TEncoding.ASCII);
  try
    Compressed := TMemoryStream.Create;
    try
      DecodeStream(Base64Stream, Compressed);
      Compressed.Position := 0;
      Utf8Stream := TStringStream.Create('', TEncoding.UTF8);
      try
        ZDecompressStream(Compressed, Utf8Stream);
        Result := Utf8Stream.DataString;
      finally
        Utf8Stream.Free;
      end;
    finally
      Compressed.Free;
    end;
  finally
    Base64Stream.Free;
  end;
end;

function TForm2.DesencriptarAES(Dato, Password: String): String;
var
  TextoPlano, TextoCodificado: String;
  Codec: TCodec;
begin
  TextoPlano := EmptyStr;
  TextoCodificado := EmptyStr;
  try
    Codec := TCodec.Create(nil);
    Codec.CryptoLibrary := TCryptographicLibrary.Create(Codec);
    Codec.AsymetricKeySizeInBits := 1024;
    Codec.StreamCipherId := 'native.StreamToBlock';
    Codec.BlockCipherId := 'native.AES-256';
    Codec.ChainModeId := 'native.CBC';

    TextoCodificado := Dato;
    Codec.Password := Password;
    Codec.DecryptString(TextoPlano, TextoCodificado, TEncoding.UTF8);

    TextoPlano := DecodeAndDecompressString(TextoPlano);
    Codec.Free;
  except on E: Exception do
    begin
      Codec.Free;
      ShowMessage(e.ToString);
    end;
  end;

  Result := TextoPlano;

end;

function TForm2.EncriptarAES(Dato, Password: String): String;
var
  TextoPlano, TextoCodificado: String;
  Codec: TCodec;
begin
  TextoPlano := EmptyStr;
  TextoCodificado := EmptyStr;
  try
    Codec := TCodec.Create(nil);
    Codec.CryptoLibrary := TCryptographicLibrary.Create(Codec);
    Codec.AsymetricKeySizeInBits := 1024;
    Codec.StreamCipherId := 'native.StreamToBlock';
    Codec.BlockCipherId := 'native.AES-256';
    Codec.ChainModeId := 'native.CBC';

    TextoPlano := Dato;

    TextoPlano := CompressAndEncodeString(TextoPlano);

    Codec.Password := Password;
    Codec.EncryptString(TextoPlano, TextoCodificado, TEncoding.UTF8);
    Codec.Free;
  except on E: Exception do
    begin
      Codec.Free;
      ShowMessage(e.ToString);
    end;
  end;
  Result := TextoCodificado;

end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  Application.OnIdle := OnIdle;
end;

procedure TForm2.OnIdle(Sender: TObject; var ADone: Boolean);
begin
  Label4.Caption := 'Longitud del Texto: '+IntToStr(Memo1.Lines.Text.Length);
  Label5.Caption := 'Longitud del Texto: '+IntToStr(Memo2.Lines.Text.Length);
end;

end.

你认为有什么可以优化的吗? 现在,代码是,原始文本为3500个字符,占用2592个字符。

非常感谢你的帮助。