关于asp中实现文件上传和下载功能(50分)

  • 主题发起人 主题发起人 shaozen
  • 开始时间 开始时间
S

shaozen

Unregistered / Unconfirmed
GUEST, unregistred user!
在一个网站的建设中,有这样的要求:用户可以进行文件的上传和现在。
请问各位如何实现啊。asp+sql
 
可以用上载组件
可以参考动网bbs的上载下传功能
 
可是使用上传组件!
我这有!
而且有我写的代码,已经实现过了!
没有问题,需要的话。。。。:)
 
谢谢 delphi_newuser。我的邮箱是shaozen@263.net
 
好了,我给你发过去了!
不要忘了给我加分!:)
 
To:delphi_newuser
能不能也给我发送一个,谢谢! pukou@gazx.com
 
to delphi_newuser
能否给我发一份:cgz0358@etang.com
 
delphi-newuser:
能不能给我发一个过来????
it_boy69@sina.com
 

用Delphi 6开发ASP上传组件详解
左轻侯
2002.6.20
  文件上传是WEB开发中经常要用到的功能,但ASP本身和内置的组件都不支持文件上传功能。网上流传的一些第三方组件虽然能够解决这个问题,但大多是要收费的,更别说Open Source了。本文将详细剖析WEB文件上传的原理,以及一步步指导读者如何用Delphi6开发一个ASP上传组件。
1 Html文件分析
  首先我们来看一个html文件源码,文件名是test.htm,功能是提供用户上传的界面:
<html>
<body>
<center>
   <form name="mainForm" enctype="multipart/form-data"
action="test.asp" method=post>
    <input type=file name=mefile><br>
<input type=hidden name=a1 value="fdsaf">
<input type=hidden name=a2 value="fdsaf">
<input type=hidden name=a3 value="fdsaf">
<input type=hidden name=a4 value="fsdfsdsaf">
<input type=hidden name=a5 value="这个是这个">
<input type=text name=a6 value="fdsaf">
   <input type=submit name=ok value="OK">
   </form>
</center>
</body>
</html>
  这个文件里包含了一个名为mainForm的form,以及随手写的一些input域。注意这个form和一般的form有两个不同的地方:一是它有一个type=file的域,没有value。用浏览器打开这个文件时,这个域会表现为一个右侧有“浏览”字样的文件输入框,用户可以通过它来选择本地硬盘上的文件。二是form有一个特殊的属性:enctype="multipart/form-data"。这个属性告诉浏览器要上传二进制文件,并进行相应编码。
  这种编码会产生什么样的表单信息呢?让我们来看看test.asp,也就是接受表单的asp文件的源码,它非常简单:
<%
formsize=request.totalbytes   '获得表单原始信息的长度
formdata=request.binaryread(formsize)   '读取表单原始信息
response.binarywrite formdata  '返回表单原始信息
%>
  如读者在注释中了解的,这段代码的功能是将表单的原始信息返回。让我们来看看它的运行效果。将这两个文件置于web目录下,访问test.htm。在文件输入框中,选择一个文件(我选了一个jpg图片,不过最大不要太大)。提交,然后可以看到这样一堆乱七八糟的信息:
-----------------------------7d2227629012e Content-Disposition: form-data;
name="mefile";
filename="C:/Documents and Settings/aaa/Mydo
cuments/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图片的编码。(实际的jpg图片编码可能要比这长得多,视文件大小而定。为了行文方便,作者只保留了一小部分。)
  分析一下这段信息的格式:  
-----------------------------7d2227629012e 这是各个域之间的分隔符。
Content-Disposition: form-data;
说明这是表单中的域。
name="mefile";
域的名称。
filename="C:/Documents and Settings/aaa/Mydo
cuments/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;
function Get_Form(Formname: OleVariant): OleVariant;
safecall;
function Get_FileName: OleVariant;
safecall;
function Get_FileSize: Integer;
safecall;
procedure FileSaveAs(FileName: OleVariant);
safecall;
function Get_FileData: OleVariant;
safecall;
function Get_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 -1do
begin
FContentData := FContentData + chr(byte(tmpvar));
end;

pos1 := pos(#13#10,FContentData);
FDelimeter := copy(FContentData,1,pos1+1);
DeliCount := length(FDelimeter);
lastpos := 1;
pos1:=0;
while pos2>=pos1do
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 pos1 <> 0 then
begin
isFile := true;
pos1 := pos1 + 10;
pos2 := instr('"',content,pos1);
FFilename := copy(content,pos1,pos2-pos1);
end;

pos1 := instr(#13#10#13#10,content,pos2+1)+4;
FormValue := copy(content,pos1,length(content)-pos1);
if isfile then
begin
FFileData := FormValue;
//查找文件类型信息
pos2 := instr('Content-Type: ',content,pos2+1);
if pos2 <> 0 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)-1do
begin
Result := Byte(FFileData[i+1]);
end;
end;

  这三个函数分别返回文件的名称、大小、数据。要注意的是,在返回文件数据时,必须进行相应的转换,将Delphi中的string类型转换为OleVariant类型。
  
procedure Tupfile.FileSaveAs(Filename: OleVariant);
var
fsout:TFileStream;
begin
fsout := TFileStream.Create(Filename,fmcreate);
try
fsout.Write(Byte(FFileData[1]),Length(FFileData))
finally
fsout.Free;
end;

end;

  这个方法将文件保存到服务器上的磁盘。
  编译myobj这个project,得到一个myobj.dll文件。开发工作就此完成。
3 使用ASP上传组件
  
  在命令行下,输入“regsvr32 myobj.dll”。弹出一个对话框,告诉你组件已经注册。如果找不到regsvr32.exe这个文件,它在windows/system32或winnt/system32目录下。
  将本文开头提到的test.asp文件修改为如下内容:
<%'建立对象
Set upfile = Server.CreateObject("myobj.upfile")
'获得表单对象
response.write upfile.form("a1")&amp;"<br>"
response.write upfile.form("a2")&amp;"<br>"
response.write upfile.form("a3")&amp;"<br>"
response.write upfile.form("a4")&amp;"<br>"
response.write upfile.form("a5")&amp;"<br>"
response.write upfile.form("a6")&amp;"<br>"
'获得文件大小
response.write "文件字节数:"&amp;upfile.filesize&amp;"<br>"
'获得文件类型
response.write "文件类型:"&amp;upfile.filetype&amp;"<br>"
'获得文件名,保存文件
upfile.filesaveas(Server.MapPath("")+upfile.filename)
set upfile = nothing
%>
  再次访问test.htm,提交表单。现在你可以看到相关的返回信息,并且在服务器上test.asp所处的目录下找到上传的文件。
  这个组件只能上传单个文件,但根据同样的原理,一次上传多个文件的功能也是不难实现的。有兴趣的读者可以自行尝试。
 


返回
 
http://www.wushuang.net/develop/upfile.zip
左轻候的ASP文件上传组件V1.0,包括已经编译好的程序和全部源代码。
[文章]用Delphi 6开发ASP上传组件详解,详细剖析WEB文件上传的原理,以及一步步指导读者如何用Delphi6开发一个ASP上传组件。
 
用ASP和VBScript上载文件
上载脚本的使用
  下面是开发的上载脚本的应用范例。本文的下载文件提供例子中的文件和代码。将压缩文件释放到一个路径下,为你的网络服务器配置一个虚拟路径。可以在浏览器中测试和启动uploadForm.html。

调用脚本
  下面是调用上载BuildUploadRequest 方法的途径。首先调用一个全程dictionary:UploadRequest。然后调用BuilUploadRequest方法,然后在自变量中传送到请求原始二进制数据。
byteCount = Request.TotalBytes
RequestBin = Request.BinaryRead(byteCount)
Dim UploadRequest
Set UploadRequest = CreateObject("Scripting.Dictionary")
BuildUploadRequest RequestBin
  数据被分解并存储在dictionary对象中,并用Item() 方法恢复。这些item 数据可以保存在VBScript 变量中,并且可以在代码的任何地方使用。数据可以作为响应传送回客户机,或用在ASP代码中,或写进文件中及放入数据库中。

取回数据
  UploadRequest 对象的数据可用Item("key") 函数进行存取。现在来考虑一下这样的情况:要存取一个 email控制的值。可以这样做:
email = UploadRequest.Item("email").Item("Value")
  因为这是一个文本类控制,内容是一个字符串,这个字符串可以同任何其它VBScript 字符串一样使用。 对于二进制数据,可以用相同方法恢复内容:
picture = UploadRequest.Item("blob").Item("Value")
  也可以存取其它信息,如文件名和content-type。他们是文本类控制。
contentType = UploadRequest.Item("blob").Item("ContentType")
filepathname = UploadRequest.Item("blob").Item("FileName")

在 VBScript 代码中使用数据
  上载的数据可以同其它变量一样在VBScript 代码中使用。比如说,它们可以作为响应发送回客户机。
Your email is : 〈%=email%〉
File name of you picture is 〈%=filepathname%〉
File type of your picture is 〈%=contentType%〉

  二进制数据也可以发送回客户机。必须设置一个content-type,可以用BinaryWrite 方法写二进制数据。
Response.ContentType = contentType Response.BinaryWrite picture

向文件中写入上载数据
  在文件类控制的情况下,目的通常是将二进制数据存入某个文件或数据库域,而不是将它们传送回客户机。这个目的是上载文件的固有特点。使用FileSystem对象将上载文件存入服务器的文件系统中。

首先创建FileSystem对象:
’Create FileSytemObject Component Set ScriptObject = Server.CreateObject("Scripting.FileSystemObject")
  用FileSystem对象在路径中创建一个文件。路径可以是绝对的,直接指向文件系统(如c: emp)。也可以是相对的,到网络服务器定义的一个虚拟路径下。用mappath方法和PATH_INFO服务器变量将虚拟路径影射到绝对路径。
  Write方法需要一个双字节字符串作为自变量,所以要将单字节数列转换成字符串。Write方法负责转换这个双字节字符串,并用ASCII 格式写它。这就建立了一个包含我们原始的单字节字符串的二进制内容的文件。我已将这个文件命名为“uploaded+filename”,这只是为了区别文件,你可以使用任何其它文件名,如:
’Create and Write to a File Set MyFile = ScriptObject.CreateTextFile(Server.mappath(Request.ServerVariables _ ("PATH_INFO")) &amp;
"uploaded" &amp;
filename)
For i = 1 to LenB(value)
MyFile.Write chr(AscB(MidB(value, i, 1)))
Next
MyFile.Close

将上载数据存入数据库
  数据还可以被存入数据库。content-type 也应该存入数据库中,以便以后显示数据。首先要建立与数据库的连接,假定已经设置了适当的DSN:
Set conn = Server.CreateObject("ADODB.Connection")
conn.open "DSN=wroxdns","user","pass"

然后从连接中创建记录集:
sql = "SELECT PHOTO, CONTENTTYPE FROM MYTABLE"
Set rs = Server.CreateObject("ADODB.Recordset")
rs.Open sql, conn, 3, 3

记录集创建之后,要将二进制数据放入数据库的blob域中:
picturechunk = picture &amp;
chrB(0)
rs.Fields("PICTURE").appendChunk picturechunk
rs.Fields("CONTENTTYPE") = contentType
rs.Update
conn.close

  在appendChunk 方法中,我不得不解决一个臭虫。事实上我注意到,当二进制数据有奇数字节时,appendChunk 方法就不传送最后一个字节。解决的办法是增加一个chr(0),以确保传送了所有的字节。也许还有其它办法,如果有的话,请告诉我。
  要得到数据库的映象,使用相同的记录集,并用正确的内容类型将它作为响应发送回客户机。
Response.contentType = rs.Fields("CONTENTTYPE")
size = rs.Fields("PICTURE").ActualSize
blob = rs.Fields("PICTURE").GetChunk(size)
Response.binarywrite blob

结论
  本文展现了一个完整的用VBScript 进行文件上载的方法。编码完全是VBScript ,独立于第三方产品

 
我用的是asp无组件上传,其过程中对服务器的cpu消耗巨大,100%
不知com组件结果如何,有测过的朋友麻烦告知结果
 
大哥,请允许我借宝地一用,谁能帮我看看这段asp.net上传错在哪啦?
我用的是控件htmlinputfile
Private Sub Button1_ServerClick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.ServerClick
File1.PostedFile.SaveAs("c:/a.jpg")
End Sub
 
后退
顶部