2017-01-20 6 views
2

Мне нужно отправить из Windows на мобильные устройства, iOS и Android по протоколу TCP, большую строку Base64.
У меня нет проблем с отправкой и получением, но размер строк слишком велик, около 24000 символов, и я ищу способ сжатия распаковки этих строк.
Глядя, я вижу, что лучший способ - использовать Zlib, и я нашел ссылку Delphi XE and ZLib Problems (II), в которой объясняется, как это сделать.
Функции работают с обычной текстовой строкой, но сжатие строк base64 делает их более большими.
Пример очень маленькой строки, я послал бы, был бы это:
Сжатие Base64 строка с zlib

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; 
+0

Я бы предположил, что наиболее эффективным подходом было бы преобразование из UTF-16 в UTF-8, а затем сжатие байтов UTF-8. Если вам нужно передать текст, base64 сжатые байты UTF-8. Сжатый текст base64 звучит неправильно. –

+0

Btw, нет XE10. Вы используете 10.1 Берлин. Я исправил текст и теги. Приятно точно указать версию. –

+0

Спасибо @DavidHeffernan за ответ, но, извините, я не понимаю, как я это делаю. – elcharlie

ответ

2

Как я понимаю, вопрос, который вы сделали следующее:

  1. Кодирование строку как UTF-8 байт.
  2. Сжат эти байты с помощью zlib.
  3. Base64 кодировал сжатые байты.

Затем вы пытаетесь сжать выход на шаге 3 и найти, что результат не меньше. Этого следует ожидать. Вы уже скомпилировали данные, и дальнейшие попытки сжать его не могут значительно уменьшить размер, особенно если вы не запустили base64. Если вы можете многократно сжимать данные и каждый раз их уменьшать, тогда, в конце концов, ничего не останется. Это, очевидно, невозможно.

Я думаю, что вы уже делаете хорошую работу. Вы конвертируете в UTF-8, который для большинства текстов является наиболее пространственным, действующим для кодировок Unicode. Если вы работаете с китайским текстом, вам будет лучше с UTF-16. Затем вы сжимаете UTF-8, что также разумно. И, наконец, для передачи вы кодируете base64, также разумно.

Самый очевидный способ уменьшить размер передаваемых данных - это опустить шаг base64. Если вы можете передавать сжатые байты, созданные на шаге 2, вы будете передавать меньше. Base64 использует 4 байта для кодирования 3 байтов, поэтому размер данных с кодировкой base64 на треть больше входных данных.

Другим способом может быть использование лучшего алгоритма сжатия, чем zlib, но опять же есть пределы того, что может быть достигнуто. И обычно лучшее сжатие достигается за счет увеличения времени вычислений.

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 символов.

Большое спасибо за помощь.

Смежные вопросы