最近在写的程序与SOAP相关,所以用到了一些Base64编码/解码及数据压缩/解压方面的知识. 在这里来作一些总结:

 
一.Base64编码/解码
 
  一般用到的是Delphi自带的单元EncdDecd,当然还有第三方提供的单元或控件,其中我所接触到的认为比较好的有Indy的TIdMimeEncode / TIdMimeDecode组件,以及RjMime单元.
 
  在这里主要想讲讲如何才能获得最好的编码/解码性能,EncdDecd提供了EncodeStream/DecodeString, EncodeString/DecodeString两对函数,如果你使用EncodeString/DecodeString,这没有什麽可争议,效率是死的,如果你使用了EncodeStream/DecodeStream,这里面可大有文章了. 先来看看两个函数的声明:
procedure EncodeStream(Input, Output: TStream);
procedure DecodeStream(Input, Output: TStream);
  很明了, 两个参数,都为TStream, TStream是抽象类, 其派生类主要有TMomoryStream,TStringStream,TFileStream等,都可以作为参数传递进去,对於Input参数,无论TMemoryStream, TStringStream, TFileStream都不会影响性能,但是对於Output参数,由於压缩的结果是写住OutputStream,因此压缩过程中不断地执行TStream的Write方法,如果是TMemoryStream,那效率真是太糟糕了,我作过测试,编码一个5M多的文件,要十几秒钟!但如果是TStringStream呢,只要0.2~0.3秒! 这究竟是为什麽呢,因为TMemoryStream里不断调用Write方法的结果是,不断地向Windows要求分配内存!从而导致性能下降!而TStringStream和TFileStream则没有这个问题. 因此,在这里极力向朋友们建议,Output参数最好不用TMemoryStream.
  不过不要紧,你一定要用的话,也是有方法解决性能下降这个问题的! 因为效率下降的原因是不断的申请内存空间,我们可以从这个方向首手,能不能一次性给它分配好内存空间呢,如果我们事先能确定编码或解码后的数据大小(字节数),那麽这是可行的. 问题的关键就是如何确定这个编码或解码后的字节数了. 对於EncdDecd,我可以给出这个计算方法:
  (1)假设编码前的字节数为X,那麽编码后的字节数为 (X + 2) div 3 * 4. 不过,要对EncdDecd进行相应的修改,找到这一小段: 
   if K > 75 then     
   begin
    BufPtr[0] := #$0D;
    BufPtr[1] := #$0A;
    Inc(BufPtr, 2);
    K := 0;
   end; 
  将其注释掉, 因为这其实是没什麽用的,只是用来对编码后的字符串分行的~,我们可以注释后将单元另存为EncdDecdEx,以后就使用它了!!!
  (2)假设解码前的字节数是X,那麽解码后的字节数约为 (X + 3) div 4 * 3
*****注:与编码不同的是,解码的字节数不是确定的,差值在0~2之间.
 
  这样我们就可以在编码/解码前对Output参数的TMemoryStream事先设置缓冲区大小了....
 
 uses
  encddecdEx; 
 var
  Input,Output:TMemoryStream;
 begin
  Input:=TMemoryStream.Create;
  try
   Input.LoadFromFile('c:\aaa.txt');
   Output:=TMemoryStream.Create;
   try
    Output.Size:=(Input.Size + 2) div 3 * 4;
    EncodeStream(Input,Output);
   finally
    Output.Free;
   end;
  finally
   Input.Free;
  end;
 end;
 
 OK! 大功告成!!! 大家有兴趣可以测试一下,加不加Output.Size:=(Input.Size + 2) div 3 * 4这一句的不同效果~
 
二.ZLib压缩/解压
 
  在一些分布式系统中,特别是Internet分布式系统,由於网络带宽所限,我们需要对传输的大流量数据进行压缩,以减轻网络的负担,加快程序运行速度,一般用到的压缩/解压方法是使用ZLib单元. ZLib单元主要提供了两个类:TCompressionStream和TDeCompressionStream. 这两个类分别处理压缩和解压缩. 使用方法可以查阅相关的资料. 在这里提供两个过程,再对压缩时的参数作些比较:
 
uses
 ZLib;
 
procedure Zip(Input,Output:TStream;Compress:Boolean);
const
 MAXBUFSIZE=1024 * 16;  //16 KB
var
 CS:TCompressionStream;
 DS:TDecompressionStream;
 Buf:array[0..MAXBUFSIZE-1] of Byte;
 BufSize:Integer;
begin
  if Assigned(Input) and Assigned(Output) then
 begin
  if Compress then
  begin
   CS:=TCompressionStream.Create(clDefault,Output);
   try
    CS.CopyFrom(Input,0); //从开始处复制
   finally
    CS.Free;
   end;
  end else
  begin
   DS:=TDecompressionStream.Create(Input);
   try
    BufSize:=DS.Read(Buf,MAXBUFSIZE);
    while BufSize>0 do
    begin
     Output.Write(Buf,BufSize);
     BufSize:=DS.Read(Buf,MAXBUFSIZE);
    end;
   finally
    DS.Free;
   end;
  end;
 end;
end;
 
function Zip(Input:string;Compress:Boolean):string;
var
 InputStream,OutputStream:TStringStream;
begin
 if Input='' then Exit;
 InputStream:=TStringStream.Create(Input);
 try
  OutputStream:=TStringStream.Create('');
  try
   Zip(InputStream,OutputStream,Compress);
   Result:=OutputStream.DataString;
  finally
   OutputStream.Free;
  end;
 finally
  InputStream.Free;
 end;
end;
 
  以上两个方法是两个名称一样,参数不同的过程. 第一个是对流进行压缩/解压,Input,Output分别是压缩/解压前的流与压缩/解压后的流. 第二个是对字符串进行压缩/解压. 两个过程都有Compress参数,这个参数用来决定进行压缩操作还是解压操作: True--压缩; false--解压.
 
  在第一个过程中,有这样一句:
  CS:=TCompressionStream.Create(clDefault,Output);
  这是在建立压缩类以对流进行压缩, 这里面有个参数clDefault,当然还有其它的选项:clNone, clFastest, clDefault, clMax;
clNone与clFastest就不讨论了,因为不能获得良好的压缩效果,在这里想讨论clDeafult与clMax哪一个好点,我作了一些测试,测试数据如下:
 
        源文件大小  压缩所用时间   压缩后文件大小
 clDefault   2.71M     ~1.4s      ~937K
         5.10M     ~2.8s      ~1.77M
 
 clMax     2.71M     ~2.5s      ~934K
         5.10M     ~4.7s      ~1.77M
 
  由这些数据可以看出,clDefault参数与clMax参数,压缩率已经非常接近了,但是所用的时间却相差了近一倍! 也就是说,差不多的压缩效率,clDefault参数比clMax参数节省了一半的时间! 因此,建议大家使用参数clDefault,这是压缩效率比较好的参数.
 
三. 何对MIDAS封包进行压缩.
 
  我们知道,MIDAS封包外在类型是OleVariant,其内部格式没有对外公开! 经过我的一些测试,该封包是以varByte为基础类型的VarArray数组. 
因此,我们可以将其转换成string类型再进行压缩,至於压缩后是以string传输还是转换回VarByte array类型,就由个人决定了. 下面的函数完成将MIDAS数据包转换成string类型.
 
function UnpackMIDAS(vData:OleVariant):string;
var
 P:Pointer;
 Size:Integer;
begin
 if not VarIsArray(vData) then Exit;
 Size:=VarArrayHighBound(vData,1)-VarArrayLowBound(vData,1)+1;
 P:=VarArrayLock(vData);
 try
  SetLength(Result,Size);
  Move(P^,Result[1],Size);
 finally
  VarArrayUnLock(vData);
 end;
end;
 
假设以下为MIDAS服务器或COM+对象一个方法.
 
function TDeptCoor.GetDeptData: OleVariant;
var
 Command:WideString;
 Options:TGetRecordOptions;
 RecsOut:Integer;
 Params,OwnerData:OleVariant;
begin
 try
  Command:='SELECT DeptID,DeptNo,DeptName,MasterID FROM Department ORDER BY DeptNo';
  Options:=[grReset,grMetaData];
  Result:=FCommTDM.AS_GetRecords('CommDsp',-1,RecsOut,Byte(Options),Command,Params,OwnerData);
  Result:=UnpackMIDAS(Result);  //将MIDAS封包转换成string类型
  Result:=Zip(Result,True);      //进行压缩,再将压缩后结果转回. 
  SetComplete;
 except
  SetAbort;
  raise;
 end;
end;
 
客户端只要压压缩后就可以使用了:
 
procedure TForm1.Button1Click(sender:TObject);
var
 vData:string;
begin
 vData:=DeptCoor.GetDeptData;
 vData:=Zip(vData,False);     //解压
 ClientDataSet1.XMLData:=vData;  //注意,这里用的是XMLData,不是Data,否则会报错!!!
end;
 
         
四. SOAP系统中压缩后编码:
 
 在SOAP系统中,由於二进制数据不能直接传递,需要进行Base64编码, 我们可以在数据传递前先压缩/Base64编码,接收后再Base64解码/解压缩.
同样,也给出两个函数,来分别完成这两个过程
 
function SoapPacket(const Input:string):string;  
var
 InputStream,OutputStream:TStringStream;
begin
 InputStream:=TStringStream.Create(Input);
 try
  OutputStream:=TStringStream.Create('');
  try
   Zip(InputStream,OutputStream,True);
   InputStream.Size:=0;
   OutputStream.Position:=0;  //很重要!!!
   EncodeStream(OutputStream,InputStream);
   Result:=InputStream.DataString;
  finally
   OutputStream.Free;
  end;
 finally
  InputStream.Free;
 end;
end;
 
function SoapUnpack(const Input:string):string;
var
 InputStream,OutputStream:TStringStream;
begin
 InputStream:=TStringStream.Create(Input);
 try
  OutputStream:=TStringStream.Create('');
  try
   DecodeStream(InputStream,OutputStream);
   InputStream.Size:=0;
   OutputStream.Position:=0; //很重要!!!
   Zip(OutputStream,InputStream,False);
   Result:=InputStream.DataString;
  finally
   OutputStream.Free;
  end;
 finally
  InputStream.Free;
 end;
end; 
 
http://www.cnblogs.com/wxy8/archive/2011/06/16/2082961.html

Delphi Base64编码/解码及ZLib压缩/解压的更多相关文章

  1. delphi Base64编码/解码及数据压缩/解压知识

    一.Base64编码/解码 一般用到的是Delphi自带的单元EncdDecd,当然还有第三方提供的单元或控件,其中我所接触到的认为比较好的有Indy的TIdMimeEncode / TIdMimeD ...

  2. java 字符串zlib压缩/解压

    今天在测公司的中间件时发现,增加netty自带的zlib codec压缩处理后,就报decompress failed, invalid head之类的异常.后来发现,直接用bytebuf处理报文体是 ...

  3. Delphi Base64编码/解码

    Uses CnBase64: CnBase64.Base64Encode(Edit1.Text, Psw64);

  4. zlib压缩解压示例

    #include <stdio.h> #include <string.h> #include <assert.h> #include "zlib.h&q ...

  5. Delphi Base64编码_解码及ZLib压缩_解压(转)

    最近在写的程序与SOAP相关,所以用到了一些Base64编码/解码及数据压缩/解压方面的知识. 在这里来作一些总结:一.Base64编码/解码 一般用到的是Delphi自带的单元EncdDecd,当然 ...

  6. 基于哈夫曼编码的压缩解压程序(C 语言)

    这个程序是研一上学期的课程大作业.当时,跨专业的我只有一点 C 语言和数据结构基础,为此,我查阅了不少资料,再加上自己的思考和分析,实现后不断调试.测试和完善,耗时一周左右,在 2012/11/19 ...

  7. OpenSSL 使用 base64 编码/解码

    简述 关于 OpenSSL 的介绍及安装请参见:Windows下编译OpenSSL 下面主要介绍有关 OpenSSL 使用 base64 编码/解码. 简述 编码解码 更多参考 编码/解码 #incl ...

  8. 利用openssl进行BASE64编码解码、md5/sha1摘要、AES/DES3加密解密

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...

  9. Javascript中Base64编码解码的使用实例

    Javascript为我们提供了一个简单的方法来实现字符串的Base64编码和解码,分别是window.btoa()函数和window.atob()函数. 1 var encodedStr = win ...

随机推荐

  1. Tomcat基础配置和高级配置

    **********  第一部分 Tomcat基础配置   *********** 一.Apatch Tomcat 在win下配置 大部分转载自:http://blog.csdn.net/liuhao ...

  2. UVA 10603 - Fill BFS~

    http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&c ...

  3. zynq修改ramdisk文件系统

    ⑴ 挂载 Ramdisk新建目录 tmp, 并将 uramdisk.image.gz 拷贝至该目录$ cd <WORKDIR>/Filesystem$ mkdir tmp$ cp uram ...

  4. Ehcache与Memcache的差别

    ehcache是纯java编写的.通信是通过RMI方式,适用于基于java技术的项目. memcachedserver端是c编写的.client有多个语言的实现,如c.php(淘宝.sina等各大门户 ...

  5. kafka集群操作指南

    目录 kafka集群操作指南 (一)单机版安装 (二)集群安装 (三)集群启停操作 (四)topic相关的操作 (五)某个broker挂掉,本机器可重启 (六)某个broker挂掉且无法重启,需要其它 ...

  6. or小计

    1.使用or的时候,必须养成两边添加括号,否则结果完全不一样. 2.or条件如果复杂的情况下,可以适当考虑union all改写.

  7. Delphi的指针(有图,很清楚)

    Pointers are like jumps, leading wildly from one part of the data structure to another. Their introd ...

  8. FOJ (FZU) 1476 矩形的个数 排列组合。

    http://acm.fzu.edu.cn/problem.php?pid=1476  Problem Description 在一个3*2的矩形中,可以找到6个1*1的矩形,4个2*1的矩形3个1* ...

  9. [Recompose] Show a Spinner While a Component is Loading using Recompose

    Learn how to use the 'branch' and 'renderComponent' higher-order components to show a spinner while ...

  10. URAL 1542. Autocompletion 字典树

    给你最多10w个单词和相应的频率 接下来最多1w5千次询问 每次输入一个字符串让你从前面的单词中依照频率从大到小输出最多10个以该字符串为前缀的单词 開始把单词建成了字典树 然后每次询问找到全部满足条 ...