用DELPHI实现文件加密压缩

Delphi教程 2025-08-21

用DELPHI实现文件加密压缩

作者:e梦缘(wnhoo)

Mail:wnhoo@163.com

风花雪月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、关键技术的讲述

(一)ZLIB

1)、基类TCustomZlibStream:是类TCompressionStream和TDecompressionStream类的基类,它主要有一个属性:OnProgress,在类进行压缩或解压缩的过程中会发生这个的事件。

格式:ProcedureOnProgress(Sender:TObject);dynamic;

2)、压缩类TCompressionStream:除了继承了基类的OnProgress属性外,又增加了一个属性:CompressionRate,它的定义如下:

PropertyCompressionRate:SinglereadGetCompressionRate;

通过这个属性,可以得到压缩比。

它的几个重要的方法定义如下:

ConstructorTCompressionStream.Create(CompressionLevel:TCompressionLevel;Dest:TStream);

其中:TcompressionLevel(压缩类型),它由如下几个定义:

1)、clNone:不进行数据压缩;

2)、clFastest:进行快速压缩,牺牲压缩效率;

3)、clDefault:进行正常压缩;

4)、clMax:进行最大化压缩,牺牲速度;

Dest:目的流,用于存放压缩过的数据。

FunctionTCompressionStream.Write(constBuffer;Count:Longint):Longint;

其中:Buffer:需要压缩的数据;

Count:需要压缩的数据的字节数;

函数返回写入流的字节数。

注意:压缩类TCompressionStream的数据只能是写入的,如果试图从其内部读取数据,将发生一个"Error"异常。需要压缩的数据通过方法Write写入流中,在写入的过程中就被压缩,并保存在由构造函数提供的内存流(TmemoryStream)中,同时触发OnProcess事件。

3)、解压缩类TDecompressionStream:和压缩类TcompressionStream相反,它的数据是只能读出的,如果试图往其内部写数据,将发生一个"Error"异常。

它的几个重要方法定义如下:

构造函数:ConstructorCreate(Source:TStream);

 其中:Source是保存着压缩数据的流;

FunctionRead(varBuffer;Count:Longint):Longint;

 数据读出函数,Buffer:存数据缓冲区;Count:缓冲区的大小;

 函数返回读出的字节数。数据在读出的过程中,数据被解压缩,并触发OnProcess事件。

(二)流

在Delphi中,所有流对象的基类为TStream类,其中定义了所有流的共同属性和方法。

TStream类中定义的属性如下:

1)、Size:此属性以字节返回流中数据大小。

2)、Position:此属性控制流中存取指针的位置。

Tstream中定义的虚方法有四个:

1)、Read:此方法实现将数据从流中读出,返回值为实际读出的字节数,它可以小于或等于指定的值。

2)、Write:此方法实现将数据写入流中,返回值为实际写入流中的字节数。

3)、Seek:此方法实现流中读取指针的移动,返回值为移动后指针的位置。

函数原形为:FunctionSeek(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:此方法的作用是从其它流中拷贝数据流。

函数原形为:FunctionCopyFrom(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类来存取文件,首先要建立一个实例。声明如下:

constructorCreate(constFilename: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文件

资源文件名:资源文件的在磁盘上存储的文件全名

例如:

myzjyexefilezjy.exe

2)、编译资源文件

在DELPHI的安装目录的/Bin下,使用BRCC32.exe编译资源文件.RC。当然,也可以将BRCC32单独拷贝到程序文档目录使用。

例如:

Brcc32wnhoo_reg.Rc

3)、资源文件引用

implementation

{$R*.dfm}

{$Rwnhoo_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;

(3)存取资源文件中的图标

将图标放在资源文件中,可以实现动态改变应用程序图标。

application.Icon.Handle:=LoadIcon(hInstance,'资源标识符');

(4)存取资源文件中的AVI

Animate.ResName:='MyAvi';//资源标识符号

Animate.Active:=True;

(5)存取资源文件中的JPEG

把jpeg单元加入到uses单元中。

var

Fjpg:TJpegImage;

FStream:TResourceStream;

begin

Fjpg:=TJpegImage.Create;

//TresourceStream使用

FStream:=TResourceStream.Create(Hinstance,'资源标识符',资源类型);

FJpg.LoadFromStream(FStream);

Image.Picture.Bitmap.Assign(FJpg);

(6)存取资源文件中的Wave

把MMSystem加入uses单元中

PlaySound(pchar('mywav'),Hinstance,Snd_ASyncorSnd_Memoryorsnd_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;

(五)文件关联

uses

registry,shlobj;

//实现关联注册

procedureTmyzip.regzzz;

var

reg: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)

图(2)

上面两种形式,可以根据实际采用。本文采用图(1)的结构。

3、对于加密后的数据,采用ZLIB技术实现压缩存储,生成新压缩形式的文件。

2.4、文件关联的实现见2.2(五)

2.5、自解压的实现

1.建立一个专门用来自解压的可执行程序文件

2.将1中建立的文件,生成资源文件

3.将资源文件放到本文中这个压缩工具的程序中一起编译。

4.通过将资源文件与压缩文件的合并,生成自解压文件。

自解压文件结构图:

5.自解压实现:通过将自身文件中的加密压缩数据的分解,然后对分解的加密压缩数据再一次解压并分解出真正的数据文件。

2.6系统程序设计

这是关于这个软件实现的核心部分全部代码,在这里详细讲述这个软件所有的技术细节。

//wnhoo_zzz.pas

unitwnhoo_zzz;

interface

uses

Windows,Forms,SysUtils,Classes,zlib,Registry,INIFILES,Dialogs,shlobj;

type

pass=string[20];

type

Tmyzip=class

private

{privatedeclarationshere}

protected

{protecteddeclarationshere}

public

procedureregzzz;

procedureys_file(infileName,outfileName:string;password:pass;isjm:boolean;ysbz:integer);

functionjy_file(infileName:string;password:pass=''):boolean;

procedurezjywj(varfilename:string);

constructorCreate;

destructorDestroy;override;

{publicdeclarationshere}

published

{publisheddeclarationshere}

end;

implementation

constructorTmyzip.Create;

begin

inheritedCreate;//初始化继承下来的部分

end;

//#####################################################

//原文件加密

procedurejm_File(vfile:string;varTarget: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,fmOpenreadorfmShareExclusive);

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,fmOpenreadorfmShareExclusive);

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;

finally

tmpstream.Free;

end;

end;

//**************************************************************

//流压缩

procedureys_stream(instream,outStream:TStream;ysbz:integer);

{

instream:待压缩的已加密文件流

outStream压缩后输出文件流

ysbz:压缩标准

}

var

ys:TCompressionStream;

begin

//流指针指向头部

inStream.Position:=0;

//压缩标准的选择

caseysbzof

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;

//*****************************************************************

//流解压

procedurejy_Stream(instream,outStream:TStream);

{

instream:原压缩流文件

outStream:解压后流文件

}

var

jyl:TDeCompressionStream;

buf:array[1..512]ofbyte;

sjread:integer;

begin

inStream.Position:=0;

jyl:=TDeCompressionStream.Create(inStream);

try

repeat

//读入实际大小

sjRead:=jyl.Read(buf,sizeof(buf));

ifsjread >0then

OutStream.Write(buf,sjRead);

until(sjRead=0);

finally

jyl.Free;

end;

end;

//**************************************************************

//实现关联注册

procedureTmyzip.regzzz;

var

reg: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;

//压缩文件

procedureTmyzip.ys_file(infileName,outfileName:string;password:pass;isjm:boolean;ysbz:integer);

{

infileName://需要压缩加密的文件

outfileName://压缩加密后产生的文件

password://解压密码

ysbz://压缩标准

}

var

instream: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;

//解压文件

functionTmyzip.jy_file(infileName:string;password:pass=''):boolean;

var

inStream,inistream,filestream_ok:TFileStream;

{

instream://解压文件名称

inistream://INI临时文件流

filestream_ok://解压OK的文件

}

outStream:tmemorystream;//临时内存流

inifile:TINIFILE;//临时INI文件

FileSize:integer;//密码文件的SIZE

resultvalue:boolean;//返回值

begin

try

inStream:=TFileStream.create(inFIleName,fmOpenRead);

try

outStream:=tmemorystream.create;

try

jy_stream(insTream,OutStream);

//生成临时INI文件

inistream:=TFileStream.create(ExtractFilePath(paramstr(0))+'tmp.in_',fmCreate);

try

//指向存储解码信息的INTEGER型变量位置

OutStream.Seek(-sizeof(FileSize),sofromend);

//读入变量信息

OutStream.ReadBuffer(FileSize,sizeof(FileSize));

//指向解码信息位置

OutStream.Seek(-(FileSize+sizeof(FileSize)),sofromend);

//将解码信息读入INI流中

inistream.CopyFrom(OutStream,FileSize);

//释放INI文件流

inistream.Free;

//读入INI文件信息

inifile:=TINIFILE.Create(ExtractFilePath(paramstr(0))+'tmp.in_');

resultvalue:=inifile.ReadBool('file1','isjm',false);

ifresultvaluethen

begin

ifinifile.ReadString('file1','password','')=trim(password)then

resultvalue:=true

else

resultvalue:=false;

end

else

resultvalue:=true;

ifresultvaluethen

begin

filestream_ok:=TFileStream.create(ExtractFilePath(paramstr(1))+inifile.ReadString('file1','filename','wnhoo.zzz'),fmCreate);

try

OutStream.Position:=0;

filestream_ok.CopyFrom(OutStream,inifile.ReadInteger('file1','filesize',0));

finally

filestream_ok.Free;

end;

end;

inifile.Free;

finally

//删除临时INI文件

deletefile(ExtractFilePath(paramstr(0))+'tmp.in_');

end;

//

finally

OutStream.free;

end;

finally

inStream.free;

end;

except

resultvalue:=false;

end;

result:=resultvalue;

end;

//自解压创建

proceduretmyzip.zjywj(varfilename:string);

var

myRes:TResourceStream;//临时存放自解压EXE文件

myfile:tfilestream;//原文件流

xfilename:string;//临时文件名称

file_ok:tmemorystream;//生成文件的内存流

filesize:integer;//原文件大小

begin

ifFileExists(filename)then

begin

//创建内存流

file_ok:=tmemorystream.Create;

//释放资源文件--自解压EXE文件

myRes:=TResourceStream.Create(Hinstance,'myzjy',Pchar('exefile'));

//将原文件读入内存

myfile:=tfilestream.Create(filename,fmOpenRead);

try

myres.Position:=0;

file_ok.CopyFrom(myres,0);

file_ok.Seek(0,sofromend);

myfile.Position:=0;

file_ok.CopyFrom(myfile,0);

file_ok.Seek(0,sofromend);

filesize:=myfile.Size;

file_ok.WriteBuffer(filesize,sizeof(filesize));

file_ok.Position:=0;

xfilename:=ChangeFileExt(filename,'.exe');

file_ok.SaveToFile(xfilename);

finally

myfile.Free;

myres.Free;

file_ok.Free;

end;

DeleteFile(filename);

filename:=xfilename;

end;

end;

//#####################################################

destructorTmyzip.Destroy;

begin

inheritedDestroy;

end;

end.

3、结束语

Delphi的全新可视化编程环境,为我们提供了一种方便、快捷的Windows应用程序开发工具。对于程序开发人员来讲,使用Delphi开发应用软件,无疑会大大地提高编程效率。在delphi中可以很方便的利用流实现文件处理、动态内存处理、网络数据处理等多种数据形式,写起程序也会大大提高效率的。

参考文献:

1、DELPHI系统帮助

2、冯志强.Delphi中压缩流和解压流的应用

3、陈经韬.谈Delphi编程中“流”