|
导读(因为我还没完成我的组件,不保证文章中的代码的正确性.)上传多文件(Sander Duivestein)介绍这是我写的一个上传文件的activex的组件.这个组件是使用winsocket 控件调用F... (因为我还没完成我的组件,不保证文章中的代码的正确性.)上传多文件 (Sander Duivestein) 介绍 这是我写的一个上传文件的activex的组件.这个组件是使用winsocket 控件调用FTP提交文件.它仅仅工作在Ie3.02或更高的版本,但有些客户端使用Netscape,因此不得不找另外的解决方案.但我搜索 Internet 和若干新闻组时,发现不止我一个有这样的要求. 在1999.3.11, 看到啦Doug Dean的一篇15Seconds的文章(关于用VB asp 组件上传文件)给我啦一个很好的提示. Doug Dean的组件简单并且易用.但他说,然而,多元件的上传问题没解决?因而仍然有一些工作要完成. 在我开始做自己的组件以前,我想知道其他类似控件提供什么样的功能.因此我看啦另外3个著名的组件: the upload component of Software Artisans, the upload component of ASPUpload, and the Microsoft Posting Acceptor. 通过比较这些组件我觉的我的组件应该满足下列要求: 提交文件的HTML表单对asp 组件应该是一黑盒子.也就是说组件能接受各种表单元件并能得到表单元件的名字和值. 它应能提供一个上传路径,并且限制大小. 组件应该能处理多个的文件. 组件应该有一错误处理程序. 组件应该性能很好. 组件应该能在NC中象IE一样工作. 保存文件进入数据库. 仅允许某组用能上载文件. 这些对我来说是有相当的挑战. 解决问题 首先我要创建一HTML文件,它包含两个元件:一简单的文本框,一文件框.这里给出下列代码: 1:Upload.htm <HTML> <HEAD><TITLE>Upload</TITLE></HEAD> <BODY> <FORM NAME="frmUpload" METHOD="Post" ENCTYPE="multipart/form-data" ACTION="Upload.asp"> <TABLE> <TR><TD>Author</TD><TD><INPUT TYPE="text" NAME="txtAuthor"></TD></TR> <TR><TD>File</TD><TD><INPUT TYPE="file" NAME="txtFileName"></TD></TR> <TR><TD COLSPAN="2" ALIGN="right"><INPUT TYPE="Submit" VALUE="Upload"></TD></TR> </TABLE> </FORM> </BODY> </HTML> 使用ENCTYPE="multipart/form-data" 使表单能够提交一文件.我们也需要一文件接收文件. 2:Upload.asp <%@ Language=VBScript %> <% Option explicit Response.Buffer = True On Error Resume Next If Request.ServerVariables("REQUEST_METHOD") = "POST" Then Dim objUpload Dim lngMaxFileBytes Dim strUploadPath Dim varResult lngMaxFileBytes = 10000 strUploadPath = "c:\inetpub\wwwroot\upload\" Set objUpload = Server.CreateObject("pjUploadFile.clsUpload") If Err.Number <> 0 Then Response.Write "The component wasn't registered" Else varResult = objUpload.DoUpload (lngMaxFileBytes, strUploadPath) Set objUpload = Nothing Dim i For i = 0 to UBound(varResult,1) Response.Write varResult(i,0) & " : " & varResult(i,1) & "<br>" Next End If End If %> 在这里设置下面两个变量: lngMaxFileBytes -文件最大字节数, 和strUploadPath -文件上传位置.我也增加了错误处理程序检查是否装入组件在网服务器上适当注册.这是我做的处理唯一的一个错误.如果任何另外的错误发生,可以再加入处理它.最后,再声明varReturn.这变量用来接受组件的返回值.这返回值应该包含所有的表单元件名字和他们的值.你能看见FOR NEXT loop中的程序,这返回值必须是一数组. 这是比较容易的部分.现在我们必须创造一ActiveX 组件,用来处理提交的表单. 打开vb6,选择一ActiveX 项目 (参阅步骤1:) 步骤1: 创造一ActiveX dll 项目 首先,先添加一个引用,在菜单条上选定添加引用项,选中 Active Server Pages Object library.(参阅步骤2). 步骤2: 工程引用 通过这个库我们能使用asp的request的请求对象.为保证能使用,要用如下代码: Option Explicit Private MyScriptingContext As ScriptingContext Private MyRequest As Request Private MyResponse As Request Public Sub OnStartPage(PassedScriptingContext As ScriptingContext) Set MyScriptingContext = PassedScriptingContext Set MyRequest = MyScriptingContext.Request Set MyResponse = MySriptingContext.Response End Sub 为什么我们需要ASP库?通过request对象我们能得到由upload.htm传来的http数据流.在那里为什么有一个 "但是"?当我们尝试读 表单字段名字和相对的值,例如,Request.Form("txtTitle"), 但我们就不能读出余下的发送给我们的原始数据.因此我们使用Request.TotalBytes 和Request.BinaryRead 读取发送的数据. 下面是我从Doug Dean得到的代码: '~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Dim varByteCount Dim binArray() As Byte '~~~~~ BYTE COUNT OF RAW FORM DATA ~~~~~~~~~~~~ varByteCount = MyRequest.TotalBytes '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ '~~~~~ PLACE RAW DATA INTO BYTE ARRAY ~~~~~~~~~ ReDim binArray(varByteCount) binArray = MyRequest.BinaryRead(varByteCount) Doug Dean将数据放入一个二进制的数组,它当然工作的很好.但是当我到想得到表单的名字和值时,我遇见一个问题,例如,我们想要提交包含5个文本框和一个文件框的HTML表单.提交的文件有100K 的大小 (=102,400字节)的限制.现在我们收到102,400字节.所有的这些字节将进一个二进制的数组.当我们想要分开时,表单元件名,他们的值,我们通过全部数组的循环.我们必须循环6次,因为我们提交了6个表单元件.它工作的很好,但是性能下降. 因此我们需要找到另一种方法从http header中提取数据.当浏览MSDN 库,我发现答案.我们必须使用Unicode!!!与Unicode 我们能把数据放进一个vb的variant,它包含字符串. 因此我的代码看起来像这: Option Explicit Private MyScriptingContext As ScriptingContext Private MyRequest As Request Private MyResponse As Request Public Sub OnStartPage(PassedScriptingContext As ScriptingContext) Set MyScriptingContext = PassedScriptingContext Set MyRequest = MyScriptingContext.Request Set MyResponse = MySriptingContext.Response End Sub Public Function DoUpload (ByVal lngMaxFileBytes As Long, _ ByVal strUploadPath As String) As Variant Dim varByteCount As Variant Dim varHTTPHeader As Variant 'Count total bytes in HTTP-header varByteCount = MyRequest.TotalBytes 'Conversion of bytes to Unicode varHTTPHeader = StrConv(MyRequest.BinaryRead(varByteCount), vbUnicode) 'Write HTTPHeader MyResponse.Write varHTTPHeader Function End 现在让我们测试这些代码的.为这测试我使用用了一个.txt 文件 "warp11.txt" 那包含的下面一句话: "Warp11 builds state-of-the-art applications at the speed of light." 我们提交的表单看起来像这: 这是上传组件写入我们的浏览器的内容: -----------------------------7cf28c330254 Content-Disposition: form-data; name="txtAuthor" Sander Duivestein -----------------------------7cf28c330254 Content- Disposition: form-data; name="txtFileName"; filename="C:\Download\Warp11.txt" Content-Type: text/plain Warp11 builds state-of-the-art applications at the speed of light. -----------------------------7cf28c330254 正如你所见的,这仅仅是一字符串,我们能识别这个字符串.当一HTML form通过multipart/form-data MME提交,表单数据被分成几部分.表单数据的每部分是由分界符分开:"-----------------------------7cf28c330254". 通过这个分界符我们能有一串不同的字符串.在上传应用程序中,我们使用这个分界符划分表单数据,因此我们把这界线字符串放在变量varDelimeter. 我们增加下列代码执行这个功能: Dim varDelimeter As Variant varDelimeter = LeftB(varHTTPHeader, 76) 首先,我们想要知道的上传了多少个表单元件.因此我们必须做一计数器: DimintFormFieldCounter As Integer 'Count formfields intFormFieldCounter = Len(varHTTPHeader) - Len(Replace(varHTTPHeader, "; name=", Mid("; name=", 2))) 当我们知道表单元件的数目时,我们能通过对varHTTPHeader 进行循环查找,然后我们能分别得到列表单元件的名字和他们的值.当我们查找某一表单域名称和值时,我们想要这些保存这些.存储这些值,我们简单地创造一个两维的数组: DimintFormFieldCounter As Integer 'Count formfields intFormFieldCounter = Len(varHTTPHeader) - Len(Replace(varHTTPHeader, "; name=", Mid("; name=", 2))) 其次,我们想要返回这些值到upload.asp 文件.这就是为什么我们使用数组变体型的类型.Variant是ASP能理解的唯一的数组类型.现在列出表单元件和他们的名字. 首先,我们创造一循环: 'Begin parsing formfield names and values For i = 0 To intFormFieldCounter - 1 然后我们决定第一表单元件将开始的位置: 'Determine where FormFieldName starts lngFormFieldNameStart = InStrB(lngFormFieldNameStart + 1, varHTTPHeader, "; name=" & Chr(34)) 我们现在找到表单元件结束的地方: 'Determine where FormFieldName ends lngFormFieldNameEnd = InStrB(lngFormFieldNameStart +_ Len(StrConv("; name=" & Chr(34), vbUnicode)), varHTTPHeader, Chr(34)) _ + Len(StrConv(Chr(34), vbUnicode)) 如果我们有表单元件开始和结束的位置,我们能过滤出表单元件的名称: 'Filter FormFieldName strFormFieldName = MidB(varHTTPHeader, lngFormFieldNameStart, lngFormFieldNameEnd - lngFormFieldNameStart) 'Remove "; name=" from string strFormFieldName = Replace(strFormFieldName, "; name=", vbNullString) 'remove quotes from string strFormFieldName = Replace(strFormFieldName, Chr(34), vbNullString) 在表单元件名字后面有一个";",通过它,我们就能找到它的类型.如果是这样,我们就能处理文件,不同的是我们正在处理一文本字段.因此建立一个IF THEN判断. 'Check for file If MidB(varHTTPHeader, lngFormFieldNameEnd, 2) = ";" Then 当看我们的HEADER, 我们能发现正准备进行的循环的条件是False.这意味着我们正在处理文本字段.现在就能找到我们的表单元件对应的值了.首先我们必须找到表单元件的值开始的地方: Else 'Determine where formfieldvalue starts lngFormFieldValueStart = lngFormFieldNameEnd 第二,和以前一样,我们必须发现我们的表单域值结束的地方: 'Determine where formfieldvalue ends lngFormFieldValueEnd = InStrB(lngFormFieldValueStart, varHTTPHeader, varDelimeter) |
温馨提示:喜欢本站的话,请收藏一下本站!