標題:用Delphi 6開發ASP上傳組件詳解

来源:互联网 发布:软件二次开发是什么 编辑:程序博客网 时间:2024/05/16 23:55

======================================================
注:本文源代码点此下载
======================================================

轉自:http://www.programfan.com

文檔中心:http://www.programfan.com/article/article.asp?classid=3

文件上传是web开发中经常要用到的功能,但asp本身和内置的组件都不支持文件上传功能。网上流

传的一些第三方组件虽然能够解决这个问题,但大多是要收费的,更别说open source了。本文将详细

剖析web文件上传的原理,以及一步步指导读者如何用delphi6开发一个asp上传组件。

源码和demo我已经发布在个人主页上

http://www.wushuang.net

1 html文件分析

首先我们来看一个html文件源码,文件名是test.htm,功能是提供用户上传的界面:

这个文件里包含了一个名为mainform的form,以及随手写的一些input域。注意这个form和一

般的form有两个不同的地方:一是它有一个type=file的域,没有value。用浏览器打开这个文件时,

这个域会表现为一个右侧有“浏览”字样的文件输入框,用户可以通过它来选择本地硬盘上的文件。二是

form有一个特殊的属性:enctype="multipart/form-data"。这个属性告诉浏览器要上传二进制文

件,并进行相应编码。

这种编码会产生什么样的表单信息呢?让我们来看看test.asp,也就是接受表单的asp文件的源码,

它非常简单:

如读者在注释中了解的,这段代码的功能是将表单的原始信息返回。让我们来看看它的运行效果。将

这两个文件置于web目录下,访问test.htm。在文件输入框中,选择一个文件(我选了一个jpg图片,

不过最大不要太大)。提交,然后可以看到这样一堆乱七八糟的信息:

-----------------------------7d2227629012e content-disposition: form-data;

name="mefile"; filename="c:\documents and settings\aaa\my documents\my

pictures\zzjh.jpg" content-type: image/pjpeg (作者注:以下为乱码)

-----------------------------7d2227629012e content-disposition: form-data;

name="a1" fdsaf -----------------------------7d2227629012e content-disposition:

form-data; name="a2" fdsaf -----------------------------7d2227629012e

content-disposition: form-data; name="a3" fdsaf

-----------------------------7d2227629012e content-disposition: form-data;

name="a4" fsdfsdsaf -----------------------------7d2227629012e

content-disposition: form-data; name="a5" 这个是这个

-----------------------------7d2227629012e content-disposition: form-data;

name="a6" fdsaf -----------------------------7d2227629012e content-disposition:

form-data; name="ok" ok -----------------------------7d2227629012e--

这就是用"multipart/form-data"方式编码的表单原始信息。其中那一段看起来是乱码的部分,就

是jpg图片的编码。

分析一下这段信息的格式:

-----------------------------7d2227629012e 这是各个域之间的分隔符。

content-disposition: form-data; 说明这是表单中的域。

name="mefile"; 域的名称。

filename="c:\documents and settings\aaa\my documents\my pictures\zzjh.jpg" 上

传文件在本地硬盘上的名称。

content-type: image/pjpeg 文件类型。

后面是文件本身的数据。

其它各个域的信息也可以以此类推。

众所周知,在asp中,使用request对象,可以访问用户提交表单的各个域。因为request对象

会对原始的表单信息进行解析,提取出表单中每个域的值。但是,request并不能解析这

"multipart/form-data"格式的表单信息。这就是asp不能直接支持文件上传的原因所在。读者可以

试试,在test.asp中,用request("mefile")这样的格式,是不能读取到正确的信息的。

问题的症结已经找到,解决的思路也很简单:用delphi开发一个com组件,接受这种原始表单信息,

将各个域一一提取出来,返回给asp文件。也就是完成request对象没有完成的功能。

2 用delphi开发组件

delphi6对开发asp组件提供了极好的支持,大大简化了我们的开发过程。

启动delphi 6,选择file-new-other-activex-activex library,这样就建立了一个

activex库。将此library改名为myobj,存盘。选择file-new-other-activex-active server

object,在coclassname中填入upfile,确定。这时会跳出一个标题为myobj.tlb的对话框,这是

delphi特有的以可视化方式编辑com接口的功能,用delphi开发过com的读者应该比较熟悉。

在myobj下的名为iupfile的interface下,添加5个属性和一个方法。如果不懂得如何操作,

请参见delphi参考书的相关部分。按f12可以看到生成的相应的myobj_tlb.pas文件,其中的

iupfile接口应该是这个样子:

iupfile = interface(idispatch)

['{5c40d0eb-5a22-4a1e-8808-62207ae04b51}']

procedure onstartpage(const ascriptingcontext: iunknown); safecall;

procedure onendpage; safecall;

functionget_form(formname: olevariant): olevariant; safecall;

functionget_filename: olevariant; safecall;

functionget_filesize: integer; safecall;

procedure filesaveas(filename: olevariant); safecall;

functionget_filedata: olevariant; safecall;

functionget_filetype: olevariant; safecall;

property form[formname: olevariant]: olevariant read get_form;

property filename: olevariant read get_filename;

property filesize: integer read get_filesize;

property filedata: olevariant read get_filedata;

property filetype: olevariant read get_filetype;

end;

其中的onstartpage方法和onendpage方法是delphi默认生成的,其它的是手动加入的。

切换到unit1.pas(也是delphi自动生成的),改名为upfile.pas存盘。可以看到存在一个

tupfile类的声明,它是继承自taspobject类和iupfile接口的。delphi 6已经自动生成了相应

的代码。接下来的任务就是实现这个接口。

除了完成iupfile接口中的属性和方法之后,还需要补充一些东西,以便完成我们的任务。最终的

tupfile类的声明如下:

tupfile = class(taspobject, iupfile)

public

protected

procedure onendpage; safecall;//页面开始

procedure onstartpage(const ascriptingcontext: iunknown); safecall;//页面

结束

procedure filesaveas(filename: olevariant); safecall;//保存文件

function get_form(formname: olevariant): olevariant; safecall;//

function get_filename: olevariant; safecall;

function get_filesize: integer; safecall;

function get_filedata: olevariant; safecall;

function get_filetype: olevariant; safecall;

private

fcontentdata:string;

ffiledata,ffilename,ffiletype:string;

fforminfo:tstringlist;

function instr(str1,str2:string;startpos:integer):integer;

procedure analyformdata(content:string);

end;

下面我们来一一分析这些成员的具体实现。

procedure tupfile.onstartpage(const ascriptingcontext: iunknown);

var

aolevariant : olevariant;

tmpvar : olevariant;

contentlength : integer;

i,delicount,pos1,pos2,lastpos : integer;

fdelimeter : string;

begin

inherited onstartpage(ascriptingcontext);

fforminfo := tstringlist.create;

contentlength := request.totalbytes;

aolevariant := contentlength;

tmpvar := request.binaryread(aolevariant);

for i := 1 to contentlength -1 do

begin

fcontentdata := fcontentdata + chr(byte(tmpvar[i]));

end;

pos1 := pos(#13#10,fcontentdata);

fdelimeter := copy(fcontentdata,1,pos1+1);

delicount := length(fdelimeter);

lastpos := 1;

pos1:=0;

while pos2>=pos1 do

begin

pos1 := instr(fdelimeter,fcontentdata,lastpos);

if pos1 = 0 then break;

pos1 := pos1 + delicount;

pos2 := instr(fdelimeter,fcontentdata,pos1)-1;

analyformdata(copy(fcontentdata,pos1,pos2-pos1-1));

lastpos := pos2;

end;

end;

前面说过,onstartpage方法是delphi自动生成的,在装载页面时发生。在这个方法中,我们完

成一些初始化的任务:读取表单的原始数据,解析表单中的域,并存入相应的属性中,以备调用。

由于delphi已经对asp中的对象进行了很好的封装,所以即使在delphi环境下,也可以方便地调

用它们,就象在asp中一样,例如request.totalbytes。首先将原始表单数据读入到一个

oleviarians类型的tmpvar中,然后通过一个循环,将它转换为delphi中的string格式,并存放

在fcontentdata中。

接下来,通过查找换行符,解析出分隔符的内容和长度。然后在一个循环中,用analyformdata成

员函数一一解析出每个域。初始化工作就这样完成了。

再看analyformdata函数的实现:

procedure tupfile.analyformdata(content: string);

var

pos1,pos2:integer;

formname,formvalue:string;

isfile:boolean;

begin

isfile := false;

pos1 := instr('name="',content,1)+6;

pos2 := instr('"',content,pos1);

formname := copy(content,pos1,pos2-pos1);

//检查是否文件

pos1 := instr('filename="',content,pos2+1);

if pos10 then

begin

pos2 := pos2 + 14;

ffiletype := copy(content,pos2,pos1-4-pos2);

end;

end

else

begin

fforminfo.add(formname+'='+formvalue);

end;

end;

如注释中所表达的,analyformdata提取原始数据中的域。如果是域是文件类型,则将文件类型和

文件数据分别放入ffiletype和ffiledata中。如果是其它类型,则将名称和值放入一个tstringlist

类型的fforminfo中。fforminfo中维护着除文件类型外的所有域的信息,以“名称=值”的格式存放。

function tupfile.get_form(formname: olevariant): olevariant;

begin

result := fforminfo.values[formname];

end;

这个函数返回域的值。只需要简单地调用fforminfo的values方法,就可以得到相应的值。这是

在tstringlist类内部实现的。

function tupfile.get_filename: olevariant;

begin

result := extractfilename(ffilename);

end;

function tupfile.get_filesize: integer;

begin

result := length(ffiledata);

end;

function tupfile.get_filedata: olevariant;

var

i:integer;

begin

result := vararraycreate( [0,length(ffiledata)], varbyte );

for i := 0 to length(ffiledata)-1 do

begin

result[i] := byte(ffiledata[i+1]);

end;

end;

这三个函数分别返回文件的名称、大小、数据。要注意的是,在返回文件数据时,必须进行相应的转

换,将delphi中的string类型转换为olevariant类型。

procedure tupfile.filesaveas(filename: olevariant);

var

fsout:tfilestream;

i:integer;

afile:file of byte;

begin

fsout := tfilestream.create(filename,fmcreate);

for i := 1 to length(ffiledata) do

begin

fsout.write(byte(ffiledata[i]),1)

end;

fsout.free;

end;

这个方法将文件保存到服务器上的磁盘。

编译myobj这个project,得到一个myobj.dll文件。开发工作就此完成。

3 使用asp上传组件

在命令行下,输入“regsvr32 myobj.dll”。弹出一个对话框,告诉你组件已经注册。如果找不到

regsvr32.exe这个文件,它在windows\system32或winnt\system32目录下。

将本文开头提到的test.asp文件修改为如下内容:

再次访问test.htm,提交表单。现在你可以看到相关的返回信息,并且在服务器上test.asp所处

的目录下找到上传的文件。

这个组件只能上传单个文件,但根据同样的原理,一次上传多个文件的功能也是不难实现的。有兴趣

的读者可以自行尝试。

申明

非源创博文中的内容均收集自网上,若有侵权之处,请及时联络,我会在第一时间内删除.再次说声抱歉!!!

博文欢迎转载,但请给出原文连接。

绿色通道:好文要顶关注我收藏该文与我联系


======================================================
在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/
原创粉丝点击