当前位置:首页 > 软件开发 > dev
firefox

用DELPHI为ASP开发文件上载组件

asp(active server page)是微软公司的产品,由于它编程很容易上手,能快速开发功能强大的动态网站,现在很多网站(特别是intranet/extranet内部网)采用了nt+iis+asp的模式,使得asp成为目前较为流行的网站开发脚本语言。在web服务中,文件上载服务是一个很常见的功能,而win9x下的pws没有提供相关组件;nt下的iis提供了一个post acceptor组件,但由于它要检查用户的www访问权限而变得不太好用;也可以从internet上下载有关组件,但这些大多都是商业组件,用于下载的是试用版,在使用时间或功能上都有限制。由于asp可以调用标准的ole/com组件,我们可以用vb/vc/delphi等高级编程工具根据我们自己的要求来定制自己的asp文件上载组件,满足自己的应用系统要求。

下面将讨论用delphi为asp开发文件上载组件的原理和具体实现过程。


一、文件上载的实现原理

基于web方式数据上传,要遵从rfc1867标准,上载的文件数据也不例外。如用下面html页面文件(delphiup.htm)选择上载文件:

<!-- delphiup.htm:文件上载界面 -->

<html><head><title>文件上载</title></head><body>

用delphi编写的文件上载组件实现文件上载

<form name="uploadform" action="delphiup.asp" method="post" enctype="multipart/form-data">

<p>文件另存为:<input type=text name="saveas">

<p>请要选择上载的文件:<input type=file name="filedata">

<input type="submit" name="b1" value="确认上载"> </p>

</form>

</body></html>

当客户端选择了一个文件(如test.txt,其内容为“这里是一个用于上载的文件的内容。”)并按

“确认上载”按钮提交数据后,服务器端程序收到的数据将具有如下形式:

-----------------------------7cf1d6c47c#13#10

content-disposition: form-data; name="saveas"#13#10#13#10

newfilename#13#10

-----------------------------7cf1d6c47c#13#10

content-disposition: form-data; name="filedata"; filename="d: est.txt"

content-type: text/plain#13#10#13#10

这里是一个用于上载的文件的内容。#13#10

-----------------------------7cf1d6c47c#13#10

content-disposition: form-data; name="b1"#13#10#13#10

确认上载#13#10

-----------------------------7cf1d6c47c--

其中,“-----------------------------7cf1d6c47c”是分界符,用于分隔表单(form)中的各个域;

#13#10是回车换行符的delphi表示。我们可以这样认为,每个表单域的信息描述,都是以分界符加一对回车换行符#13#10开始;表单域名以“name="”开始,以“"”为结束;表单域值以两对回车换行符#13#10#13#10开始,以一对回车换行符#13#10#加分界符结束;文件名称以“filename="”开始,以“"”为结束。有了这些标志,我们就可以获取表单域的名称和值以及要上载的文件的名称,从而实现文件数据的读取和存储了。


二、文件上载的实现过程

在理解上面提到的数据格式后,自己动手编写一个文件上载组件对我们来说已经不是困难了。


(一)开始建立一个asp组件的工程

如果您对用delphi开发ole automation server的步骤不太熟悉的话,请参见《电子与电脑》1999年第06期的一篇文章《用delphi开发用于asp的ole automation server 》。

这里只简要介绍一下操作步骤。

1、建立activex library工程

在delphi中选择菜单file=》new...,在“new item”对话框的activex选项卡中选择“activex library”,delphi会自动创建一个dll工程project1。

2、建立automation组件

在delphi中选择菜单file=》new...,在“new item”对话框的activex选项卡中选择“automation object”;然后在“automation object wizard”对话框中输入class name(如“uploadfile”),instancing选择“multiple instance”即可,单击“ok”后delphi会自动创建一个tlb(type library)文件project1_tlb.pas和一个pas(unit)文件unit1.pas。在type library设计窗口中,将project1改名为myupload,则该文件上载组件的ole注册码为“myupload.uploadfile”。

3、引入asp类型库

为了使用asp的五个内建对象(request、response、server、application、session),需要引入asp类型库。我们主要利用request对象读取从客户端传递到服务器端的数据。

在project菜单中选择“import type library”,在“import type library”对话框的“type libraries”列表选择“microsoft active server pages object library(version 2.0)”(如果没有这个选项,请确定您的计算机上安装了iis3以上或pws4以上并且asp.dll已正确注册),delphi会自动创建一个tlb文件asptypelibrary_tlb.pas,其中有我们需要的asp对象类型声明。

4、定义onstartpage、onendpage过程

当在asp页面上用server.createobject创建一个ole对象实例时,web服务器会调用其方法onstartpage,将asp应用环境信息传递给该对象,我们可以在该过程中获取客户端信息;当在asp页面中释放一个ole对象实例时,web服务器会调用其方法onendpage,我们可以在该过程中进行释放内存等结束操作。在我们这个组件中,我们要用到其onstartpage方法。

onstartpage方法应该在unit1.pas中定义,onstartpage的函数原型为:

procedure onstartpage(ascriptingcontext: iunknown);

其中参数ascriptingcontext是一个iscriptingcontext类型变量,包含五个属性(request、response、server、application、session)分别对应asp的五个内建同名对象。

我们需要在tlb定义窗口(view=》type library)中,为iuploadfile增加方法onstartpage,其declaration语句为“procedure onstartpage(ascriptingcontext: iunknown);”。


(二)提取客户端上传的数据

该工作可以放在onstartpage过程中进行。

利用ascriptingcontext的属性request(类型为irequest)中的属性totalbytes(请求信息内容长度)和方法binaryread可将客户端上传的请求信息数据读取到一个byte类型的数组中,然后按rfc1867标准定义的数据格式来分析和提取数据。

1、首先定义tuploadfile的几个私有变量

在单元文件up01.pas(由unit1.pas另存)中加入对asptypelibrary_tlb.pas的引用(uses),

然后加入

private

fcontentlength : longint;//请求信息内容长度

fcontentdata : variant;//内容数据,以数组形式存储请求信息内容

ffilename, //要上载的文件名称

fdelimeter : string; //表单域分界符

fscriptingcontext : iscriptingcontext;//asp处理上下文环境内容

ffiledatastart, //文件数据开始位置

ffiledataend : longint; //文件数据结束位置


2、提取客户端上传的请求信息数据

//在onstartpage事件中,获取asp上下文信息、请求信息内容、表单域的分界符、文件数据

procedure tuploadfile.onstartpage(ascriptingcontext: iunknown);

var

arequest : irequest; //www请求对象

aolevariant : olevariant; //记录请求信息内容长度

intdelimterlength : integer;//分界符长度

longindex,alongint,longpos : longint;

contentdata : ansistring;//请求信息内容的字符串表示

strtemp : string;

findendoffiledata : boolean;//是否找到文件数据结束位置

begin

//提取客户端上传的请求信息数据

fscriptingcontext := ascriptingcontext as iscriptingcontext;//获取asp上下文信息

arequest := fscriptingcontext.request;//获取www请求信息

fcontentlength := arequest.totalbytes;//请求信息内容长度

//创建动态数组,用于以数组形式存储请求信息内容

fcontentdata := vararraycreate( [0,fcontentlength], varbyte );

//将请求信息内容存储到数组中

aolevariant := fcontentlength;

fcontentdata := arequest.binaryread( aolevariant );//读取请求信息内容

//将请求信息内容转化为字符串,便于定位

contentdata := ;

for longindex := 0 to fcontentlength - 1 do

begin

contentdata := contentdata + chr( byte( fcontentdata[ longindex ] ));

if fcontentdata[ longindex ] = 0 then break;//0表示内容结束

end;


3、获取分界符、上载文件名称

//获取表单域的分界符

longpos := pos( #13#10,contentdata );//回车换行符所在位置

fdelimeter := copy( contentdata,1,longpos-1);//该位置之前的内容为分隔符


//获取带源路径的文件名称,在请求信息内容中,文件名称以

//filename="path/filename"的形式存储

strtemp := filename=";//文件名称在“filename="”之后

longpos := pos( strtemp, contentdata );//获取“filename="”位置

if longpos <= 0 then

begin

ffilename := ;

ffiledatastart := -1;

ffiledataend := -2;

exit;

end;

//获取下个双引号“"”之前的内容,即带源路径的文件名称

longpos := longpos + length( strtemp );

strtemp := ;

for longindex := longpos to fcontentlength - 1 do

if contentdata[ longindex ] <> " then

strtemp := strtemp + contentdata[ longindex ]

else break;

ffilename := strtemp;


4、获取文件数据的在请求信息内容中的开始、结束位置

//文件数据开始位置在文件名称后的第一个#13#10#13#10之后

delete( contentdata, 1, longindex );

strtemp := #13#10#13#10;

ffiledatastart := longindex + pos(strtemp, contentdata) + length(strtemp) - 1;


//文件数据结束位置在下一个#13#10和分界符之前

//由于文件数据可能包含非法字符,不能再用字符串定位函数pos

//查找下一个分界符的位置

ffiledataend := ffiledatastart;

intdelimterlength := length( fdelimeter );

findendoffiledata := false;

while ffiledataend <= fcontentlength - intdelimterlength do

begin

findendoffiledata := true;

for alongint := 0 to intdelimterlength - 1 do

if byte( fdelimeter[ alongint + 1 ] ) <>

fcontentdata[ ffiledataend + alongint ] then

begin

findendoffiledata := false;

break;

end;

if findendoffiledata then break;

ffiledataend := ffiledataend + 1;

end;

if not findendoffiledata then ffiledataend := ffiledatastart - 1//未找到分界符

else ffiledataend := ffiledataend - 3;//分界符,向前跳过#13#10

end;


(三)向asp程序传递信息

在进行了(二)的操作之后,我们的上载组件可以根据asp程序的要求向其传递数据了。目前可以提供的数据有:客户端源文件名称(ffilename,含路径)、文件大小(ffiledataend-ffiledatastart+1)。

首先应该在tlb设计窗口中声明如下两个方法getfilename和getfilesize。

1、返回客户端源文件名称(含路径)

//返回客户端源文件名称(含路径)

function tuploadfile.getfilename: olevariant;

begin

result := ffilename;//客户端源文件名称(含路径)

end;

2、返回文件大小

//返回文件大小(bytes)

function tuploadfile.getfilesize: olevariant;

begin

result := ffiledataend - ffiledatastart + 1;

end;


(四)保存文件

在进行了(二)的操作之后,我们的上载组件可以根据asp程序的要求保存文件了。首先应该在

tlb设计窗口中声明如下两个方法savefileas和savefile。

1、按指定文件名称保存文件

//按指定的文件名称保存文件,参数filename为指定的文件名称,返回值true表示文件保存成功

function tuploadfile.savefileas(filename: olevariant): olevariant;

var

longindex : longint;

afile : file of byte;//以二进制的形式保存文件

bytedata : byte;

begin

result := true;

try

assign( afile, filename );

rewrite( afile );

for longindex := ffiledatastart to ffiledataend do

begin

bytedata := byte( fcontentdata[ longindex ] );

write( afile, bytedata );

end;

closefile( afile );

except

result := false;

end;

end;

2、按缺省文件名称保存文件

//按缺省文件名称保存文件,将文件以同名文件保存在调用页面所在目录

function tuploadfile.savefile: olevariant;

var

currentfilepath : string;

begin

//获取调用页面所在目录

currentfilepath := fscriptingcontext.request.servervariables[path_translated];

currentfilepath := extractfilepath( currentfilepath );

//保存文件

result := savefileas( currentfilepath + extractfilename( ffilename ));

end;


三、上载组件应用举例

在我们的例子中,delphiup.htm是文件上载界面,delphiup.asp用来执行文件上载操作。

delphiup.asp的代码如下:

<!--delphiup.asp:文件上载处理页面-->

<html><head><title>文件上载</title></head><body>

<% dim upload, filename

set upload = server.createobject("myupload.uploadfile")

filename = upload.getfilename

response.write "<br>正在保存文件《"&filename&"》......"

if upload.savefile then

response.write "<br>文件《"&filename&"》上载成功。"

response.write "<br>文件大小为"&upload.getfilesize&"字节。"

else

response.write "<br>文件《"&filename&"》上载失败。"

end if

set upload=nothing %>

</body></html>


四、几点说明

1、由delphi自动生成的源代码编译的dll文件大小有215k,可以在

asptypelibrary_tlb.pas的interface段中将uses中的单元除activex外全部删除,在

myupload_tlb.pas中删除uses中所有单元,则生成的dll文件大小可减少到61k。

2、以上方法同样适用于cgi程序,不过要用twebrequest对象。


以上程序在pwin98+delphi3.0+pws4.0下调试通过。

 ↓相关文章:
© 2006-2008 All Rights Reserved