谁有上传下载的源码或控件?能断点续传的最好
解决方案 »
- [缺少更新或刷新的键列信息]的错误
- Delphi怎样创建非模态窗口
- 如果数据库有数据更新,在dbgrid里如何即时的看到数据的更新?高手指点一下。
- DELPHI6.0 程序有时运行出错
- 怎样把listview中数据导入到EXCEL表中??
- 请问:如何在程序里动态加入n个控件?
- 几个关于Delphi发展历史的问题?
- 如何使用installshield制作delphi+ado+access編寫的安裝程序﹖謝謝﹗
- 关于动态菜单的解决方法
- delphi中对应与VBScript中的MID函数的是什么?
- 请问我这样的winsock程序能支持多用户同时连接吗?
- socket总是连不上服务器,各位大哥帮帮我啊。。。。。
---- 黄明建的“编写Web方式上载文件的ASP组件”(以下简称原文)一文从RFC1867标准出发,分析了上传的二进制文件格式,编写出了具有文件上载功能的ASP组件,突破了多以Perl语言来实现WEB方式上载文件的方法。但原文忽略了以下几个关键问题: 按照RFC1867标准,上载的文件可以更换新名。在原文的demo.htm中,文本框text1即起到保存新文件名的作用。原文却忽略了这一点,使得text1成为一个并无实际意义的元素; ActiveX代码部件(ASP组件)的接口函数(类的公有成员函数)是强类型检查,即必须指定形参的具体数据类型而不能使用自动类型(Variant);但在ASP环境中,所有的变量实际上都是Variant类型,如何使ASP的变量可以作为ASP组件接口函数的实参将关系到ASP组件是否具有实际的应用价值。原文也忽略了这一技术要点; ASP组件毕竟是基于服务器端的,诸如空提交等的验证应首先在客户端采用VBScript/JavaScript来完成,以避免无用信息的传递。
---- 基于以上几点,本文首先升级了原文的ASP组件版本,使其成为一个功能完备的ASP文件上载组件。
---- 现将原文ASP组件中接口的规范说明总结如下: ---- 1、 功能:取得提交表单中元素的值;
---- 方法名:GetTheValue;
---- 形参:提交表单中元素名称,nm(数据类型:字符串);
---- 返回值:成功,则为指定的元素在提交表单中的值(数据类型:字符串);
----------- 失败,则返回零长度字符串。 ---- 2、 功能:保存上载文件
---- 方法名:SaveTheFile
---- 形参:1、提交表单中文件类型元素名称,strTag(数据类型:字符串);
--------- 2、保存目标文件的绝对路径,末尾不带“\”,strPath(数据类型:字符串);
---- 返回值:成功,则为保存的目标文件名(数据类型:字符串);
----------- 失败,则返回零长度字符串。 ---- 本文升级的ASP组件与原文的ASP组件保持二进制兼容,即保留了原版本的全部接口,并增加了增强的接口,其规范说明如下: ---- 3、 功能:增强型保存上载文件,建议不再使用方法SaveTheFile
---- 方法名:ExSaveTheFile
---- 形参:1、提交表单中文件类型元素名称,strTag(数据类型:字符串);
--------- 2、保存目标文件的绝对路径,末尾不带“\”,strPath(数据类型:字符串);
--------- 3、保存的目标文件名(支持长文件名),strDesFileName(数据类型:字符串);
---- 此参数可选参数,未指定时,以源文件名作为目标文件名;
---- 返回值:成功,则为保存的目标文件名(数据类型:字符串);
----------- 失败,则返回零长度字符串。 ---- 在ASP上载文件组件的新版本中,调用方法ExSaveTheFile如忽略可选取参数strDesFilename,则可以实现与方法SaveTheFile相同的功能;选取参数时,目标文件名既可以是用户指定的新文件名,也可以根据用户ID或cookie及数据库中的信息来生成,具有较强的实际应用价值。 ---- 值得一提的是,在ActiveX DLL工程属性中应设置版本的二进制兼容并选择一个合适的基地址;另外,VB6提供一个打包和展开向导(Package & Deployment Wizard)用于将ActiveX部件打包成CAB文件并可部署到远程IIS服务器上。 ---- 其次,解决ASP编程中调用ActiveX部件的参数类型匹配问题。由于ASP环境中变量都是Variant类型,直接以之作为ASP上载文件组件的实参就会产生类型不匹配的错误。根据笔者的经验和对ActiveX部件的反复测试,找到了以下解决方法: ---- 如果VarPara是ASP环境中的变量,则“” & VarPara可以匹配用VB编写的ActiveX部件中string数据类型。
一、文件上载的实现原理 基于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#10Content-Disposition: form-data; name="SaveAs"#13#10#13#10NewFileName#13#10-----------------------------7cf1d6c47c#13#10Content-Disposition: form-data; name="FileData"; filename="D:\test.txt" Content-Type: text/plain#13#10#13#10这里是一个用于上载的文件的内容。#13#10-----------------------------7cf1d6c47c#13#10Content-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);”。
2、提取客户端上传的请求信息数据//在OnStartPage事件中,获取ASP上下文信息、请求信息内容、表单域的分界符、文件数据procedure TUploadFile.OnStartPage(AScriptingContext: IUnknown);varARequest : 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 dobeginContentData := 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 thenbeginFFileName := '';FFileDataStart := -1;FFileDataEnd := -2;exit;end;//获取下个双引号“"”之前的内容,即带源路径的文件名称longPos := longPos + length( strTemp );strTemp := '';for longIndex := longPos to FContentLength - 1 doif ContentData[ longIndex ] <> '"' thenstrTemp := 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 dobeginFindEndOfFileData := true;for ALongInt := 0 to intDelimterLength - 1 doif Byte( FDelimeter[ ALongInt + 1 ] ) <> FContentData[ FFileDataEnd + ALongInt ] thenbeginFindEndOfFileData := false;break;end;if FindEndOfFileData then break;FFileDataEnd := FFileDataEnd + 1;end;if not FindEndOfFileData then FFileDataEnd := FFileDataStart - 1//未找到分界符else FFileDataEnd := FFileDataEnd - 3;//分界符,向前跳过#13#10end;
(三)向ASP程序传递信息 在进行了(二)的操作之后,我们的上载组件可以根据ASP程序的要求向其传递数据了。目前可以提供的数据有:客户端源文件名称(FFileName,含路径)、文件大小(FFileDataEnd-FFileDataStart+1)。首先应该在TLB设计窗口中声明如下两个方法GetFileName和GetFileSize。 1、返回客户端源文件名称(含路径)//返回客户端源文件名称(含路径)function TUploadFile.GetFileName: OleVariant;beginresult := FFileName;//客户端源文件名称(含路径)end; 2、返回文件大小//返回文件大小(Bytes)function TUploadFile.GetFileSize: OleVariant;beginresult := FFileDataEnd - FFileDataStart + 1;end;
(四)保存文件 在进行了(二)的操作之后,我们的上载组件可以根据ASP程序的要求保存文件了。首先应该在TLB设计窗口中声明如下两个方法SaveFileAs和SaveFile。 1、按指定文件名称保存文件//按指定的文件名称保存文件,参数FileName为指定的文件名称,返回值True表示文件保存成功function TUploadFile.SaveFileAs(FileName: OleVariant): OleVariant;varlongIndex : LongInt;AFile : file of byte;//以二进制的形式保存文件byteData : Byte;beginresult := true;tryassign( AFile, FileName );rewrite( AFile );for longIndex := FFileDataStart to FFileDataEnd dobeginbyteData := Byte( FContentData[ longIndex ] );Write( AFile, byteData );end;CloseFile( AFile );exceptresult := false;end;end; 2、按缺省文件名称保存文件//按缺省文件名称保存文件,将文件以同名文件保存在调用页面所在目录function TUploadFile.SaveFile: OleVariant;varCurrentFilePath : 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, FileNameset Upload = Server.CreateObject("MyUpload.UploadFile")FileName = Upload.GetFileNameResponse.Write "<br>正在保存文件《"&FileName&"》......"if Upload.SaveFile thenResponse.Write "<br>文件《"&FileName&"》上载成功。"Response.Write "<br>文件大小为"&Upload.GetFileSize&"字节。"elseResponse.Write "<br>文件《"&FileName&"》上载失败。"end ifset 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下调试通过。
unit Unit1;interfaceuses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs,winInet,ComCtrls, StdCtrls;type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
function FtpDownloadFile(strHost, strUser, strPwd: string;
Port: Integer; ftpDir: string): Boolean;
function FindFiles(FFtphandle:HINTERNET):TStringList;
private
{ Private declarations }
public
{ Public declarations }
end;var
Form1: TForm1;implementation{$R *.dfm}procedure TForm1.Button1Click(Sender: TObject);
begin
ftpdownloadfile('192.168.0.253','root','',21,'/mnt/imagefolder');
end;
function tform1.FtpDownloadFile(strHost, strUser, strPwd: string;
Port: Integer; ftpDir: string): Boolean;
var
hNet, hFTP: HINTERNET;
bSuccess: Boolean;
str:tstringlist;
i:integer;
begin
Result := False; hNet := InternetOpen('Program_Name', // Agent
INTERNET_OPEN_TYPE_PRECONFIG, // AccessType
nil, // ProxyName
nil, // ProxyBypass
0); // or INTERNET_FLAG_ASYNC / INTERNET_FLAG_OFFLINE
if hNet = nil then
begin
memo1.Lines.Add('缺少动态连接库WinInet.Dll!');
Exit;
end; { Connect to the FTP Server }
hFTP := InternetConnect(hNet, // Handle from InternetOpen
PChar(strHost), // FTP server
port, // (INTERNET_DEFAULT_FTP_PORT),
PChar(StrUser), // username
PChar(strPwd), // password
INTERNET_SERVICE_FTP, // FTP, HTTP, or Gopher?
INTERNET_FLAG_PASSIVE, // flag: 0 or INTERNET_FLAG_PASSIVE
0);// User defined number for callback
if hFTP = nil then
begin
InternetCloseHandle(hNet);
memo1.Lines.add(strhost+'不存在!');
Exit;
end; { Change directory }
bSuccess := FtpSetCurrentDirectory(hFTP, PChar(ftpDir));
if not bSuccess then
begin
InternetCloseHandle(hFTP);
InternetCloseHandle(hNet);
memo1.Lines.Add('操作抓拍机错误,请检查!');
Exit;
end;
try
str:=tstringlist.Create;
str.Clear;
str:=findfiles(hftp);
memo1.Lines.Add(inttostr(str.Count));
//for i:=0 to str.Count-1 do
//ftpgetfile(hftp,PChar(str.Strings[i]),pchar('e:\ftptest\'+str.Strings[i]),false,File_Attribute_Normal,Ftp_Transfer_Type_Binary,0);
finally
str.Free;
InternetCloseHandle(hFTP);
InternetCloseHandle(hNet);
Result := True;
end;
function Tform1.FindFiles(FFtphandle:HINTERNET):tstringlist;
var
FindData: TWin32FindData;
FindHandle: HInternet;
FCurFiles:tstringlist;
i:integer;
begin
FindHandle := FtpFindFirstFile(FFtphandle, '*.jpg',
FindData, 0, 0);
if FindHandle = nil then begin
Result := nil;
Exit;
end;
fcurfiles:=tstringlist.Create;
FCurFiles.Clear;
//i:=0;
while InternetFindnextFile(FindHandle, @FindData) do
begin
FCurFiles.Add(FindData.cfilename);
//memo1.Lines.Add(FindData.cfilename);
//inc(i);
end;
//InternetCloseHandle(Findhandle);
Result := FCurFiles;
end;
end.