不知道为什么,我解压缩的时候CompressedStream.ReadBuffer(Count, sizeof(Count));这句话老是报流读取错误,压缩解压缩方法都是网上普遍的代码啊!!! 怎么回事?????

解决方案 »

  1.   

    无意间看到的一篇文章,不知道是否有用:
    用DELPHI实现文件加密压缩wnhoo(e梦缘) 概述:在这篇文件中,讲述对单个文件的数据加密、数据压缩、自解压的实现。同样,也可以实现对多个文件或文件夹的压缩,只要稍加修改便可实现。 关键字:加密压缩、Zlib、流、资源文件 引 言:在日常中,我们一定使用过WINZIP、WINRAR这样的出名的压缩软件,就是我们开发软件过程中不免要遇到数据加密、数据压缩的问题!本文中就这一技术问题展开探讨,同时感谢各位网友的技巧,在我每次面对问题要解决的时候,是你们辛苦地摸索出来的技巧总是让我豁然开朗,问题迎刃而解。本篇文章主要是运用DELPH的强大的流处理方面的技巧来实现的数据加密压缩,并用于实际的软件程序开发中,将我个人的心得、开发经验写出来与大家分享。 1、   系统功能1)、数据压缩使用DELPHI提供的两个流类(TCompressionStream和TDecompressionStream)来完成数据的压缩和解压缩。2)、数据加密压缩通过Delphi编程中“流”的应用实现数据加密,主要采用Tstream的两个派生类Tfilestream、Tmemorystream 来完成的;其中数据压缩部分采用1)的实现方法3)、双击压缩文件自动关联解压通过更改注册表的实现扩展名与程序文件的关联,主要采用Tregistry;并且,API函数SHChangeNotify实现注册效果的立即呈现。4)、可生成自解压文件自解压的文件实现数据压缩1)与数据加密压缩2)的自动解压;并且,通过资源文件的使用实现可执行的自解压文件与数据文件的合并,来完成数据的自解压实现。 2、  系统实现2.1、工作原理
     2.2、关键技术的讲述(一)ZLIB1)、基类 TCustomZlibStream:是类TCompressionStream和TDecompressionStream 类的基类,它主要有一个属性: OnProgress,在类进行压缩或解压缩的过程中会发生这个的事件。
    格式:Procedure OnProgress (Sender: TObject); dynamic;2)、压缩类TCompressionStream:除了继承了基类的OnProgress 属性外,又增加了一个属性:CompressionRate,它的定义如下:
    Property CompressionRate: Single read GetCompressionRate;
    通过这个属性,可以得到压缩比。它的几个重要的方法定义如下:
    Constructor TCompressionStream.Create (CompressionLevel: TCompressionLevel; Dest: TStream);
    其中:TcompressionLevel(压缩类型),它由如下几个定义:
       1)、 clNone :不进行数据压缩;
       2)、 clFastest:进行快速压缩,牺牲压缩效率;
       3)、 clDefault:进行正常压缩;
       4)、 clMax: 进行最大化压缩,牺牲速度;
    Dest:目的流,用于存放压缩过的数据。Function TCompressionStream.Write (const Buffer; Count: Longint): Longint;
    其中:Buffer:需要压缩的数据;
       Count: 需要压缩的数据的字节数;
    函数返回写入流的字节数。注意:压缩类TCompressionStream的数据只能是写入的,如果试图从其内部读取数据,将发生一个"Error "异常。需要压缩的数据通过方法 Write写入流中,在写入的过程中就被压缩,并保存在由构造函数提供的内存流(TmemoryStream)中,同时触发 OnProcess 事件。3)、 解压缩类 TDecompressionStream :和压缩类TcompressionStream相反,它的数据是只能读出的,如果试图往其内部写数据,将发生一个"Error "异常。
    它的几个重要方法定义如下:
    构造函数:Constructor Create(Source: TStream);
      其中:Source 是保存着压缩数据的流;Function Read(var Buffer; Count: Longint): Longint;
      数据读出函数,Buffer: 存数据缓冲区;Count: 缓冲区的大小;
      函数返回读出的字节数。数据在读出的过程中,数据被解压缩,并触发 OnProcess 事件。   (二)流在Delphi中,所有流对象的基类为TStream类,其中定义了所有流的共同属性和方法。TStream类中定义的属性如下:1)、Size:此属性以字节返回流中数据大小。2)、Position:此属性控制流中存取指针的位置。 Tstream中定义的虚方法有四个:1)、Read:此方法实现将数据从流中读出,返回值为实际读出的字节数,它可以小于或等于指定的值。2)、Write:此方法实现将数据写入流中,返回值为实际写入流中的字节数。3)、Seek:此方法实现流中读取指针的移动,返回值为移动后指针的位置。
    函数原形为:Function Seek(Offset:Longint;Origint:Word):Longint;virtual;abstract;参数Offset为偏移字节数,参数Origint指出Offset的实际意义,其可能的取值如下:soFromBeginning:Offset为指针距离数据开始的位置。此时Offset必须大于或者等于零。soFromCurrent:Offset为移动后指针与当前指针的相对位置。soFromEnd:Offset为移动后指针距离数据结束的位置。此时Offset必须小于或者等于零。4)、Setsize:此方法实现改变数据的大小。 另外,TStream类中还定义了几个静态方法:1)、ReadBuffer:此方法的作用是从流中当前位置读取数据,跟上面的Read相同。
    注意:当读取的数据字节数与需要读取的字节数不相同时,将产生EReadError异常。2)、WriteBuffer:此方法的作用是在当前位置向流写入数据,跟上面的Write相同。
    注意:当写入的数据字节数与需要写入的字节数不相同时,将产生EWriteError异常。3)、CopyFrom:此方法的作用是从其它流中拷贝数据流。
    函数原形为:Function CopyFrom(Source:TStream;Count:Longint):Longint;参数Source为提供数据的流,Count为拷贝的数据字节数。当Count大于0时,CopyFrom从Source参数的当前位置拷贝Count个字节的数据;当Count等于0时,CopyFrom设置Source参数的Position属性为0,然后拷贝Source的所有数据; Tstream常见派生类:TFileStream       (文件流的存取)TStringStream     (处理内存中的字符串类型数据)TmemoryStream     (对于工作的内存区域数据处理)TBlobStream       (BLOB类型字段的数据处理)TwinSocketStream  (socket的读写处理)ToleStream        (COM接口的数据处理)TresourceStream   (资源文件流的处理)其中最常用的是TFileStream类。使用TFileStream类来存取文件,首先要建立一个实例。声明如下:constructor Create(const Filename:string;Mode:Word);Filename为文件名(包括路径)Mode为打开文件的方式,它包括文件的打开模式和共享模式,其可能的取值和意义如下:打开模式:fmCreate :用指定的文件名建立文件,如果文件已经存在则打开它。fmOpenRead :以只读方式打开指定文件fmOpenWrite :以只写方式打开指定文件fmOpenReadWrite:以写写方式打开指定文件共享模式:fmShareCompat :共享模式与FCBs兼容fmShareExclusive:不允许别的程序以任何方式打开该文件fmShareDenyWrite:不允许别的程序以写方式打开该文件fmShareDenyRead :不允许别的程序以读方式打开该文件fmShareDenyNone :别的程序可以以任何方式打开该文件  (三)资源文件1)、创建资源文件 首先创建一个.Rc的纯文本文件。
    格式: 资源标识符  关键字  资源文件名  资源标识符:程序中调用资源时的特殊标号; 关键字:标识资源文件类型;Wave: 资源文件是声音文件;RCDATA: JPEG文件;AVI: AVI动画;ICON: 图标文件; BITMAP: 位图文件; CURSOR: 光标文件; EXEFILE : EXE文件资源文件名:资源文件的在磁盘上存储的文件全名 例如:myzjy exefile zjy.exe 2)、编译资源文件在DELPHI的安装目录的\Bin下,使用BRCC32.exe编译资源文件.RC。当然,也可以将BRCC32单独拷贝到程序文档目录使用。例如:Brcc32 wnhoo_reg.Rc 3)、资源文件引用…implementation {$R *.dfm}{$R wnhoo_reg.Res}…4)、调用资源文件(1)存取资源文件中的位图(Bitmap) Image.Picture.Bitmap.Handle :=LoadBitmap(hInstance,'资源标识符');注:如果位图没有装载成功,程序仍旧执行,但是Image将不再显示图片。你可以根据LoadBitmap函数的返回值判断是否装载成功,如果装载成功返回值是非0,如果装载失败返回值是0。 另外一个存取显示位图的方法如下  Image.Picture.Bitmap.LoadFromResourceName(hInstance,'资源标识符');  (2)存取资源文件中的光标Screen.Cursors[]是一个光标数组,使用光标文件我们可以将定制的光标加入到这个属性中。因为默认的光标在数组中索引值是0,所以除非想取代默认光标,最好将定制的光标索引值设为1。 Screen.Cursors[1] :=LoadCursor(hInstance,'资源标识符');Image.Cursor :=1;
      

  2.   

    (3)存取资源文件中的图标将图标放在资源文件中,可以实现动态改变应用程序图标。Application.Icon.Handle := LoadIcon(hInstance,'资源标识符'); (4)存取资源文件中的AVIAnimate.ResName :='MyAvi' ; //资源标识符号Animate.Active :=True ; (5)存取资源文件中的JPEG把jpeg单元加入到uses单元中。varFjpg : TJpegImage ;FStream :TResourceStream ;beginFjpg :=TJpegImage.Create ;//TresourceStream使用FStream := TResourceStream.Create (Hinstance,'资源标识符',资源类型) ;FJpg.LoadFromStream (FStream) ;Image.Picture.Bitmap.Assign (FJpg); (6)存取资源文件中的Wave把MMSystem加入uses单元中PlaySound(pchar('mywav'),Hinstance,Snd_ASync or Snd_Memory or snd_Resource) ; (四)INI文件操作(1)     INI文件的结构:;这是关于INI文件的注释部分[节点]关键字=值...INI文件允许有多个节点,每个节点又允许有多个关键字, “=”后面是该关键字的值(类型有三种:字符串、整型数值和布尔值。其中字符串存贮在INI文件中时没有引号,布尔真值用1表示,布尔假值用0表示)。注释以分号“;”开头。 (2)     INI文件的操作1、   在Interface的Uses节增加IniFiles;2、   在Var变量定义部分增加一行:inifile:Tinifile;然后,就可以对变量myinifile进行创建、打开、读取、写入等操作了。3、   打开INI文件:inifile:=Tinifile.create('tmp.ini');4、   读取关键字的值:
    a:=inifile.Readstring('节点','关键字',缺省值);// string类型
    b:=inifile.Readinteger('节点','关键字',缺省值);// integer类型
    c:=inifile.Readbool('节点','关键字',缺省值);// boolean类型
    其中[缺省值]为该INI文件不存在该关键字时返回的缺省值。5、   写入INI文件:
    inifile.writestring('节点','关键字',变量或字符串值);
    inifile.writeinteger('节点','关键字',变量或整型值);
    inifile.writebool('节点','关键字',变量或True或False);
    当这个INI文件的节点不存在时,上面的语句还会自动创建该INI文件。6、   删除关键字:
    inifile.DeleteKey('节点','关键字');//关键字删除
    inifile.EraseSection('节点');// 节点删除7、   节点操作:
    inifile.readsection('节点',TStrings变量);//可将指定小节中的所有关键字名读取至一个字符串列表变量中;
    inifile.readsections(TStrings变量);//可将INI文件中所有小节名读取至一个字符串列表变量中去。
    inifile.readsectionvalues('节点',TStrings变量);//可将INI文件中指定小节的所有行(包括关键字、=、值)读取至一个字符串列表变量中去。8、   释放:inifile.distory;或inifile.free; (五)文件关联usesregistry, shlobj;//实现关联注册procedure Tmyzip.regzzz;varreg: TRegistry;begin  reg := TRegistry.Create;  reg.RootKey := HKEY_CLASSES_ROOT;  reg.OpenKey('.zzz', true);  reg.WriteString('', 'myzip');  reg.CloseKey;  reg.OpenKey('myzip\shell\open\command', true);  //用于打开.zzz文件的可执行程序  reg.WriteString('', '"' + application.ExeName + '" "%1"');  reg.CloseKey;  reg.OpenKey('myzip\DefaultIcon',true);  //取当前可执行程序的图标为.zzz文件的图标  reg.WriteString('',''+application.ExeName+',0');  reg.Free;  //立即刷新  SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nil, nil); end; 2.3、加密压缩的实现1、  生成INI临时加密文件用于加密的INI的临时文件格式:[FILE1]//节点,在软件中使用FILE1..N可以实现多文件加密FILENAME=压缩文件名PASSWORD=解压密码FILESIZE=文件大小FILEDATE=创建日期ISJM=解压是否需要密码如果是实现多文件、文件夹的信息存储,可以将密码关键字存在一个总的节点下。本文中仅是实现对单个文件的加密,所以只要上述格式就可以了。2、   将数据文件与用于加密的INI文件的合并,这可以采用文件流的形式实现。加密后文件结构图:图(1)原文件
     
    加密信息
     
    加密信息SIZE
     
      图(2)原文件
     
    加密信息
     
    加密信息SIZE
     
      上面两种形式,可以根据实际采用。本文采用图(1)的结构。3、   对于加密后的数据,采用ZLIB技术实现压缩存储,生成新压缩形式的文件。 2.4、文件关联的实现见2.2 (五) 2.5、自解压的实现1.   建立一个专门用来自解压的可执行程序文件2.   将1中建立的文件,生成资源文件3.   将资源文件放到本文中这个压缩工具的程序中一起编译。4.   通过将资源文件与压缩文件的合并,生成自解压文件。自解压文件结构图:加密压缩后的[数据文件]
     
    自解压文件
     
    数据文件SIZE
    5.自解压实现:通过将自身文件中的加密压缩数据的分解,然后对分解的加密压缩数据再一次解压并分解出真正的数据文件。
      

  3.   

    2.6 系统程序设计
    这是关于这个软件实现的核心部分全部代码,在这里详细讲述这个软件所有的技术细节。// wnhoo_zzz.pas unit wnhoo_zzz;interface uses  Windows,Forms,SysUtils,Classes,zlib,Registry,INIFILES, Dialogs, shlobj;type  pass=string[20];type  Tmyzip = class  private{ private declarations here}protected{ protected declarations here }public  procedure regzzz;  procedure ys_file(infileName, outfileName: string;password:pass;isjm:boolean;ysbz:integer);  function jy_file(infileName: string;password:pass=''):boolean;  procedure zjywj(var filename:string);  constructor Create;  destructor Destroy; override;{ public declarations here }published{ published declarations here }end; implementation constructor Tmyzip.Create;begininherited Create; // 初始化继承下来的部分end; //#####################################################//原文件加密procedure jm_File(vFile:string;var Target:TMemoryStream;password:pass;isjm:boolean);{vfile:加密文件target:加密后输出目标流》》》password:密码isjm:是否加密-------------------------------------------------------------加密后文件SIZE=原文件SIZE+[INI加密压缩信息文件]的SIZE+存储[INI加密压缩信息文件]的大小数据类型的SIZE---------------------------------------------------------------}var tmpstream,inistream:TFileStream;FileSize:integer;inifile:TINIFILE;filename:string;begin//打开需要 [加密压缩文件]tmpstream:=TFileStream.Create(vFile,fmOpenread or fmShareExclusive);try//向 [临时加密压缩文件流] 尾部写入 [原文件流]Target.Seek(0,soFromEnd);Target.CopyFrom(tmpstream,0);//取得文件路径,生成 [INI加密压缩信息文件]filename:=ExtractFilePath(paramstr(0))+'tmp.in_';inifile:=TInifile.Create(filename);inifile.WriteString('file1','filename',ExtractFileName(vFile));inifile.WriteString('file1','password',password);inifile.WriteInteger('file1','filesize',Target.Size);inifile.WriteDateTime('file1','fileDate',now());inifile.WriteBool('file1','isjm',isjm);inifile.Free ;//读入 [INI加密压缩信息文件流]inistream:=TFileStream.Create(filename,fmOpenread or fmShareExclusive);  try  //继续在 [临时加密压缩文件流] 尾部加入 [INI加密压缩信息文件]  inistream.Position :=0;  Target.Seek(0,sofromend);  Target.CopyFrom(inistream,0);  //计算当前 [INI加密压缩信息文件] 的大小  FileSize:=inistream.Size ;  //继续在 [临时加密文件尾部] 加入 [INI加密压缩信息文件] 的SIZE信息  Target.WriteBuffer(FileSize,sizeof(FileSize));  finally  inistream.Free ;  deletefile(filename);  end;finallytmpstream.Free;end;  end; //************************************************************** //流压缩procedure ys_stream(instream, outStream: TStream;ysbz:integer);{instream: 待压缩的已加密文件流outStream  压缩后输出文件流ysbz:压缩标准}var  ys: TCompressionStream;begin  //流指针指向头部  inStream.Position := 0;  //压缩标准的选择  case ysbz of  1:  ys := TCompressionStream.Create(clnone,OutStream);//不压缩  2:  ys := TCompressionStream.Create(clFastest,OutStream);//快速压缩  3:  ys := TCompressionStream.Create(cldefault,OutStream);//标准压缩  4:  ys := TCompressionStream.Create(clmax,OutStream); //最大压缩  else   ys := TCompressionStream.Create(clFastest,OutStream);  end;   try    //压缩流    ys.CopyFrom(inStream, 0);  finally    ys.Free;  end;end; //*****************************************************************  //流解压procedure jy_Stream(instream, outStream: TStream);{instream :原压缩流文件outStream:解压后流文件}var  jyl: TDeCompressionStream;  buf: array[1..512] of byte;  sjread: integer;begin  inStream.Position := 0;  jyl := TDeCompressionStream.Create(inStream);  try    repeat      //读入实际大小      sjRead := jyl.Read(buf, sizeof(buf));      if sjread > 0 then        OutStream.Write(buf, sjRead);    until (sjRead = 0);  finally    jyl.Free;  end;end;  //************************************************************** //实现关联注册procedure Tmyzip.regzzz;varreg: TRegistry;begin  reg := TRegistry.Create;  reg.RootKey := HKEY_CLASSES_ROOT;  reg.OpenKey('.zzz', true);  reg.WriteString('', 'myzip');  reg.CloseKey;  reg.OpenKey('myzip\shell\open\command', true);  //用于打开.zzz文件的可执行程序  reg.WriteString('', '"' + application.ExeName + '" "%1"');  reg.CloseKey;  reg.OpenKey('myzip\DefaultIcon',true);  //取当前可执行程序的图标为.zzz文件的图标  reg.WriteString('',''+application.ExeName+',0');  reg.Free;  //立即刷新  SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nil, nil); end; //压缩文件procedure Tmyzip.ys_file(infileName, outfileName: string;password:pass;isjm:boolean;ysbz:integer);{infileName://需要压缩加密的文件outfileName://压缩加密后产生的文件password://解压密码ysbz://压缩标准}varinstream:TMemoryStream;   //文件加密后的临时流outStream: TFileStream;   //压缩输出文件流 begin    //创建 [文件加密后的临时流]    instream:=TMemoryStream.Create;    //文件加密    jm_file(infileName,instream,password,isjm);    //创建压缩输出文件流    outStream := TFileStream.create(outFIleName, fmCreate);    try     //[文件加密后的临时流] 压缩     ys_stream(instream,OutStream,ysbz);    finally      OutStream.free;      instream.Free ;    end;end;
      

  4.   

    谢谢楼上的,说的很详细,不过网上传的解压缩的方法和你的不一样,不是用循环,而是读出大小,分配内存空间,然后解压到新的内存空间里,这样应该快些吧!!!!(我只是吧bmp又换成了流而已)
    procedure UnCompressBitmap(const CompressedStream: TFileStream; var Bmp: TBitmap);     var
        SourceStream: TDecompressionStream;
          DestStream: TMemoryStream;
          Buffer: PChar;
    Count: Integer;
         Begin
         file://从被压缩的图像流中读出原始图像的尺寸
          CompressedStream.ReadBuffer(Count, SizeOf(Count));
          file://根据图像尺寸大小为将要读入的原始图像流分配内存块
          GetMem(Buffer, Count);      DestStream := TMemoryStream.Create;
          SourceStream := TDecompressionStream.Create(CompressedStream);
         Try
          file://将被压缩的图像流解压缩,然后存入 Buffer内存块中
          SourceStream.ReadBuffer(Buffer^, Count);
          file://将原始图像流保存至 DestStream流中
          DestStream.WriteBuffer(Buffer^, Count);
          DestStream.Position := 0;//复位流指针
          //从 DestStream流中载入原始图像流
          Bmp.LoadFromStream(DestStream);
          finally
          FreeMem(Buffer);
          DestStream.Free;
         end;    end;
      

  5.   

    如此之麻烦,我自己写的压缩和解压缩函数,接口你一看就明白了,要给分啊
    //===================================压缩函数=========================================
    //压缩比定义:TCompressionLevel = (clNone, clFastest, clDefault, clMax);
    function CompressFile(sSourceFile,sZipFile: string; const CompressLevel: TCompressionLevel): boolean;
    var
      SourceFileStream: TFileStream;
      CompressStream: TCompressionStream;
      MemStream,DestMemStream: TMemoryStream;
      Buffer: PChar;
      Count,MemSize: Longint;
    begin
      try
        SourceFileStream := TFileStream.Create(sSourceFile, fmOpenRead); //打开源文件
        Count := SourceFileStream.size;                                  //读出源文件大小
        GetMem(Buffer, Count);
        
        MemStream := TMemoryStream.Create;
        DestMemStream := TMemoryStream.Create;
        CompressStream := TCompressionStream.Create(CompressLevel, DestMemStream);
        try
          SourceFileStream.ReadBuffer(Buffer^, Count);       //将源文件内容读到Buffer中
          MemStream.WriteBuffer(Buffer^, Count);             //将Buffer内容读到MemStream中
          MemSize := MemStream.Size;                         //记录原始文件尺寸
          MemStream.SaveToStream(CompressStream);            //将MemStream内容压缩保存在DestMemStream中
          CompressStream.Free;
          MemStream.Clear;                                   //清空MemStream内容
          MemStream.WriteBuffer(MemSize, SizeOf(MemSize));   //写入原始文件尺寸
          MemStream.CopyFrom(DestMemStream, 0);              //将压缩过的文件流DestMemStream内容拷贝到MemStream中
          MemStream.SaveToFile(sZipFile);                    //将MemStream内容写到文件
          result := true;
        finally
          FreeMem(Buffer);
          MemStream.Free;  DestMemStream.Free;  SourceFileStream.Free;
        end;
      except
        result := false;
      end;
    end;//===============================解压缩函数====================================================
    function UnCompressFile(sZipFile,sUnZipFile: String): boolean;
    var
      ZipStream: TFileStream;
      MemStream: TMemoryStream;
      DecompressStream: TDecompressionStream;
      Buffer: PChar;
      Count: Longint;
    begin
      try
        ZipStream := TFileStream.Create(sZipFile, fmOpenRead); //打开压缩文件
        ZipStream.ReadBuffer(Count, SizeOf(Count));            //将压缩文件的原始尺寸读到Count中
        GetMem(Buffer, Count);                                 //根据文件的原始尺寸大小给Buffer分配空间    MemStream := TMemoryStream.Create;
        DecompressStream := TDecompressionStream.Create(ZipStream);
        try
          DecompressStream.ReadBuffer(Buffer^, Count);         //将文件解压缩到Buffer中
          MemStream.WriteBuffer(Buffer^, Count);               //将Buffer内容写到MemStream中
          MemStream.SaveToFile(sUnZipFile);                    //将MemStream写到文件
          result := true;
        finally
          FreeMem(Buffer);
          DecompressStream.Free;    MemStream.Free;    ZipStream.Free;
        end;
      except
        result := false;
      end;
    end;