最近在写的程序与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 + ) div * ;
    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= * ;  //16 KB
var
 CS:TCompressionStream;
 DS:TDecompressionStream;
 Buf:array[..MAXBUFSIZE-] 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,); //从开始处复制
   finally
    CS.Free;
   end;
  end else
  begin
   DS:=TDecompressionStream.Create(Input);
   try
    BufSize:=DS.Read(Buf,MAXBUFSIZE);
    while BufSize> 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,)-VarArrayLowBound(vData,)+;
 P:=VarArrayLock(vData);
 try
  SetLength(Result,Size);
  Move(P^,Result[],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',-,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:=;
   OutputStream.Position:=;  //很重要!!!
   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:=;
   OutputStream.Position:=; //很重要!!!
   Zip(OutputStream,InputStream,False);
   Result:=InputStream.DataString;
  finally
   OutputStream.Free;
  end;
 finally
  InputStream.Free;
 end;
end;

Delphi Base64编码_解码及ZLib压缩_解压(转)的更多相关文章

  1. python用模块zlib压缩与解压字符串和文件的方法

    摘自:http://www.jb51.net/article/100218.htm Python标准模块中,有多个模块用于数据的压缩与解压缩,如zipfile,gzip, bz2等等. python中 ...

  2. python 模块zlib 压缩与解压

    例子1:压缩与解压字符串 import zlib message = 'abcd1234' compressed = zlib.compress(message) decompressed = zli ...

  3. 浅谈在c#中使用Zlib压缩与解压的方法

    作者:Compasslg 介绍 近期用c#开发一个游戏的存档编辑工具需要用 Zlib 标准的 Deflate 算法对数据进行解压. 在 StackOverflow 上逛了一圈,发现 c# 比较常用到的 ...

  4. Delphi编码与签名【URL编码与解码,Base64编码与解码,MD5加密,HMAC-SHA1、HMAC-SHA224、HMAC-SHA256、HMAC-SHA384和HMAC-SHA512签名】

    作者QQ:(648437169) 点击下载➨delphi编码与签名 [Delphi编码与签名]URL编码与解码,Base64编码与解码,MD5加密,HMAC-SHA1.HMAC-SHA224.HMAC ...

  5. Java 8中的Base64编码和解码

    转自:https://juejin.im/post/5c99b2976fb9a070e76376cc Java 8会因为将lambdas,流,新的日期/时间模型和Nashorn JavaScript引 ...

  6. Kotlin/Java Base64编码和解码(图片、文件)

    原文: Kotlin/Java Base64编码和解码(图片.文件) | Stars-One的杂货小窝 最近在项目中使用到了Base64编码和解码,便是稍微写篇文章记录一下 PS:本文代码都是使用Ko ...

  7. BASE64编码和解码(VC源代码) 并 内存加载 CImage 图像

      BASE64可以用来将binary的字节序列数据编码成ASCII字符序列构成的文本.完整的BASE64定义可见 RFC1421和 RFC2045.编码后的数据比原始数据略长,为原来的4/3.在电子 ...

  8. base64编码、解码的C语言实现

    转自:http://www.cnblogs.com/yejianfei/archive/2013/04/06/3002838.html base64是一种基于64个可打印字符来表示二进制数据的表示方法 ...

  9. android Java BASE64编码和解码二:图片的编码和解码

    1.准备工作 (1)在项目中集成 Base64 代码,集成方法见第一篇博文:android Java BASE64编码和解码一:基础 (2)添加 ImgHelper 工具类 package com.a ...

随机推荐

  1. Java排序算法 [选择、冒泡、快排]

    选择排序: 简述:从数组的第一个元素开始,依次与其他所有的元素对比,如果比自身大或小(取决于升序或降序)交换位置. package com.sort; import java.util.Arrays; ...

  2. setTimeout()和setInterval()的用法及区别

    setInterval 方法表示每间隔一段时间执行一次函数,会一直执行下去,除非手动 clearInterval 来停止. var i = 10; var timer = setInterval(() ...

  3. selenuim模块的使用 解析库

    selenium: 是自动化测试工具,我们可以用它来进行爬虫. 可以驱动浏览器,执行自定义好的任务. 可以执行js代码 执行速度慢,效率低. 一般用于做登录的认证 基本选择器: find_elemen ...

  4. One Switch for Mac 一键切换系统各项功能

        One Switch 是火球工作室推出的最新 Mac效率软件,它在 Menubar 菜单里集成了隐藏桌面(图标).切换 Dark Mode.保持亮屏.开启屏保的一键切换按钮,将以往这些以独立小 ...

  5. Ubuntu下Arm-Linux-GCC交叉编译环境的搭建

    1.下载arm-linux-gcc-3.4.1.tar.bz2到临时的目录下. 2.解压 arm-linux-gcc-3.4.1.tar.bz2 #tar -jxvf arm-linux-gcc-3. ...

  6. vue常见面试题

    什么是 mvvm? MVVM 是 Model-View-ViewModel 的缩写.mvvm 是一种设计思想.Model 层代表数据模型,也可以在 Model 中定义数据修改和操作的业务逻辑:View ...

  7. [CSP-S模拟测试]:抽卡(概率DP)

    题目描述 水上由岐最近在肝手游,游戏里有一个氪金抽卡的活动.有$n$种卡,每种卡有 3 种颜色.每次抽卡可能什么也抽不到,也可能抽到一张卡.每氪金一次可以连抽 m 次卡,其中前$m−1$次抽到第$i$ ...

  8. SpringMVC·form表单Date类型问题导致的400问题

    问题描述 前端传yyyy-MM-dd hh:mm:ss格式的时间其实是String类型导致JavaBean中的Date类型Setter报错,从而导致api请求400. 问题解决 我的解决方式: 在对应 ...

  9. JQ-jQuery-Ajax:jQuery Ajax 操作函数

    ylbtech-JQ-jQuery-Ajax:jQuery Ajax 操作函数 1.返回顶部 1. jQuery Ajax 操作函数 jQuery 库拥有完整的 Ajax 兼容套件.其中的函数和方法允 ...

  10. HTML-参考手册: HTML 事件属性

    ylbtech-HTML-参考手册: HTML 事件属性 1.返回顶部 1. HTML 事件属性 全局事件属性 HTML 4 的新特性之一是可以使 HTML 事件触发浏览器中的行为,比方说当用户点击某 ...