==============================================================
第三天:得到所有的文本单元
wake up!继续啦~~~~~
昨天,我们已经找到了得到一个单元的信息的办法,不过,还没有到实用阶段。毕竟,想实用,还至少要:
对于文本单元,能按名称检索的内容;
对于文件单元,能按名称得到文件的具体内容、类型、全路径、以及大小等信息。
今天,我们就首先着手解决文本单元的问题。
得到内容也许不难,可是,怎么组织才能使这个过程井井有条,才能符合我们的一般习惯?我们可以从现有的知识里找答案。
大家都知道,asp有一个内置对象request,他的功能是得到用户请求的相关信息,包括表单域的值。粗看上去,他的form集合的用法和我们要实现的得到文本单元内容的功能是很近似的,我们来看看request.form的几个应用的例子:
得到表单域的值 -
request.form("表单域名称")或request.form("表单域在<form></form>里的序号")
得到同名表单域的各个元素 -
request.form("表单域名称")(i)或request.form("表单域在<form></form>里的序号")(i)
得到同名表单域的个数 -
request.form("表单域名称").Count或request.form("表单域在<form></form>里的序号").Count
如果我们能够用ourRequest.form("name"),ourRequest.form(index),ourRequest.form("name").count,ourRequest.form(index).count这样的方式,或是与之相近的方式,不就可以很好的和request对象对应起来么?而且,因为对request对象本身的熟悉,也会降低使用我们自己方法的时候的门槛,相对于写一堆getvalue(name)函数这样的方法,更不容易出错,扩展性更好更灵活,可读性也好得多。那么,我们就看看如果要实现自己的request对象,都有哪些工作要做。
首先,ourRequest应该是一个对象,有自己的属性和方法。只有这样,才可能和现有的request对象做呼应。在vbs5里面,已经可以通过Class关键字,来实现自己的类了,所以,可行性上是没有问题的,只要我们自己定义一个类,然后实例化他,就可以得到我们所需的对象;
其次,因为ourRequest.form可以用名称和序号检索,所以,应该提供比较丰富的访问方式;
第三,在表单里有多个域名称相同的时候(比如多个checkbox),应该能够得到其中的各个元素,并且可以得到总个数。所以,ourRequest.form()得到的,应该也是一个可以检索的对象,而且有Count属性。
最终,结合vbscript的语言特点,兼顾开发效率,我们决定实现这样的几个类:
A。UploadRequest
这个类和request对象是对应的
属性:
RawData 得到原始数据,方便检查[只读]
Forms 得到一个有count属性的计数器,
可以用outRequest.Forms.Count的方式,得到文本表单域的的个数[只读]
Form(index) 可以用数字或文本检索文本表单域,做用类似request.form。
他返回一个FormElement型的对象
B。FormElement
可以把它看成单个表单域的化身。通过这个类,可以得到详细的表单域信息,比如name,value等等。如果有多个value(比如checkbox的情况),还可以选择用序号索引
属性:
value 得到表单域的值。如果有多个(比如checkbox),
返回所有的,用逗号分隔[默认]
Name 得到表单域的名称
Item(index) 用数字索引多个值中的某一个
Count 得到对应一个name,所拥有的value的个数。主要用于checkbox[只读]
C。Counter
一个辅助类,就是为了实现outRequest.Forms.Count功能。这里写的并不好,不过考虑大家的理解方便,先暂时这样。
属性:
Count 得到Count
方法:
setCount 设置Count
下面,我们就来看看这几个类的实现:
<%
Class FormElement
’ m_开头,表示类成员变量。
Private m_dicItems
Private Sub Class_Initialize()
Set m_dicItems = Server.CreateObject("Scripting.Dictionary")
End Sub
’ count是咱们这个类的一个只读属性
Public Property Get Count()
Count = m_dicItems.Count
End Property
’ value是一个默认属性。目的是得到值
Public Default Property Get value()
value = Item("")
End Property
’ Name是得到文本域名称。就是<input name=xxx>里的xxx
Public Property Get Name()
Keys = m_dicItems.Keys
Name = Keys(0)
Name = left(Name,instrrev(Name,"_")-1)
End Property
’ Item属性用来得到重名表单域(比如checkbox)的某一个值
Public Property Get Item(index)
If isNumeric(index) Then ’是数字,合法!
If index > m_dicItems.Count Then
err.raise 1,"IndexOutOfBound", "表单元素子集索引越界"
End If
Itms = m_dicItems.Items
Item = Itms(index)
ElseIf index = "" Then ’没给值?那就返回所有的!逗号分隔
Itms = m_dicItems.Items
For i = 0 to m_dicItems.Count-1
If i = 0 Then
Item = Itms(0)
Else
Item = Item & "," & Itms(i)
End If
Next
Else ’给个一个不是数字的东东?出错!
err.raise 2,"IllegalArgument", "非法的表单元素子集索引"
End If
End Property
Public Sub Add(key, item)
m_dicItems.Add key, item
End Sub
End Class
Class UploadRequest
Private m_dicForms
Private m_bFormdata
Private Sub Class_Initialize()
Set m_dicForms = Server.CreateObject("Scripting.Dictionary")
Call fill()
End Sub
’ 有了这个,就可以检查原始数据了
Public Property Get RawData()
RawData = m_bFormdata
End Property
’ 这一段丑陋的代码是为了实现outRequest.Forms.Count这个功能。
Public Property Get Forms()
Set Forms = New Counter
Forms.setCount(m_dicForms.Count)
End Property
Public Property Get Form(index)
If isNumeric(index) Then ’是数字?用数字来检索
If index > m_dicForms.Count Then
err.raise 1,"IndexOutOfBound", "表单元素索引越界"
End If
Items = m_dicForms.Items
Set Form = Items(index)
ElseIf VarType(index) = 8 Then ’字符串?也行!
If m_dicForms.Exists(index) Then ’存在,就返回值
Set Form = m_dicForms.Item(index)
Else ’不存在,就给个空值——request对象就是这么做的。
Exit Property
End If
Else ’给了一个不是数字也不是字符串的东东?出错!
err.raise 2,"IllegalArgument", "非法的表单元素索引"
End If
End Property
Private Sub fill
’ 得到数据
m_bFormdata=request.binaryread(request.totalbytes)
’ 调用这个函数实现递归循环,读取文本单元
Call fillEveryFirstPart(m_bFormdata)
End Sub
Private Sub fillEveryFirstPart(data)
’ 这就是name="
const_nameis=chrb(110)&chrb(97)&chrb(109)&chrb(101)&chrb(61)&chrb(34)
’ 这就是filename="
const_filenameis=chrb(102)&chrb(105)&chrb(108)&chrb(101)&_
chrb(110)&chrb(97)&chrb(109)&chrb(101)&chrb(61)&chrb(34)
’ 这是回车<return>
bncrlf=chrb(13) & chrb(10)
’ 得到divider,分隔符
divider=leftb(data,instrb(data,bncrlf)-1)
’ 起始位置
startpos = instrb(data,divider)+lenb(divider)+lenb(bncrlf)
’ 终止位置,从起始位置开始到下一个divider
endpos = instrb(startpos, data, divider)-lenb(bncrlf)
If endpos < 1 Then ’没有下一个了!结束!
Exit Sub
End If
part1 = midb(data, startpos, endpos-startpos)
’ 得到part1的第一行
firstline = midb(part1, 1, instrb(part1, bncrlf)-1)
’没有filename=",有name=",说明是一个文本单元(这里有一个BUG,自己研究一下?当作业吧)
If Not instrb(firstline, const_filenameis) > 0_
And instrb(firstline, const_nameis) > 0 Then
’ 得到表单域名称,就是<input type=sometype name=somename>里的somename。
fldname = B2S(midb(part1,_
instrb(part1, const_nameis)+lenb(const_nameis),_
instrb(part1, bncrlf)_
-instrb(part1, const_nameis)-lenb(const_nameis)-1))
’ 得到表单域的值
fldvalue = B2S(midb(part1,_
instrb(part1, bncrlf&bncrlf)+lenb(bncrlf&bncrlf),_
lenb(part1)-instrb(part1, bncrlf&bncrlf)+lenb(bncrlf&bncrlf)))
If m_dicForms.Exists(fldname) Then
Set fElement = m_dicForms.Item(fldname)
m_dicForms.Remove fldname
Else
Set fElement = new FormElement
End If
fElement.Add fldname&"_"&fElement.Count, fldvalue
m_dicForms.Add fldname, fElement
End If
’ 截取剩下的部分,递归调用这个函数,来得到下一个part1。
Call fillEveryFirstPart(rightb(data, lenb(data)-endpos-1))
End Sub
’ 这是一个公用函数,作用是二进制和字符串的转换
Private Function B2S(bstr)
If not IsNull(bstr) Then
for i = 0 to lenb(bstr) - 1
bchr = midb(bstr,i+1,1)
If ascb(bchr) > 127 Then ’遇到了双字节,就得两个字符一起处理
temp = temp & chr(ascw(midb(bstr, i+2, 1) & bchr))
i = i+1
Else
temp = temp & chr(ascb(bchr))
End If
next
End If
B2S = temp
End Function
End Class
’ 这是一个辅助类,为了实现outRequest.Forms.Count功能。
Class Counter
Private m_icnt
’ count是咱们这个类的一个只读属性
Public Property Get Count()
Count = m_icnt
End Property
Public Function setCount(cnt)
m_icnt = cnt
End Function
End Class
%>
<%
’下面是测试码
set outRequest = new UploadRequest
%>
<%=outRequest.Form(0).Name%>:<%=outRequest.Form("file1_desc")%><br>
<%=outRequest.Form(1).Name%>:<%=outRequest.Form("file2_desc")%><br>
<%=outRequest.Form(2).Name%>:<%=outRequest.Form(2).Count%><br>
<%=outRequest.Form(3).Name%>:<%=outRequest.Form(3)%><hr>
一共有<%=outRequest.Forms.Count%>个文本单元
这里的注释很详细,而且,每一个类的属性和方法都很少,所以相信基础好的朋友读懂是没有问题的。对应的,我们的测试表单也改成了:
<form action="doupload.asp" method=post enctype="multipart/form-data">
file1说明:<input type=text name=file1_desc>
file1<input type=file name=file1><br>
file2说明:<input type=text name=file2_desc>
file2<input type=file name=file2><br>
<input type=checkbox name=chk value=a>a
<input type=checkbox name=chk value=b>b
<input type=checkbox name=chk value=c>c
<input type=checkbox name=chk value=d>d
<input type=checkbox name=chk value=e>e<hr>
<input type=submit name=upload value=upload>
</form>
注意,这里的每一个文本表单域都要填上,因为测试码给得很特殊,读了0,1,2,3各个项目的值,测试了各个属性。不过,现实情况下,因为事先知道表单域的名称;即使不知道,也可以用outRequest.Forms.Count来循环读取,所以是没问题的,不容易出错。
现在,试试看!怎么样?成功了吧 呵呵,中英文都没有问题,用法也很简单,很清晰。现在,我们就可以说基本上解决了文本域的读取问题。
--------------------------------------------------------
今天这一段是很有挑战性的。我写了两个多小时。对于尚处于初级的朋友,可能会觉得有些吃力。其实,关键在于深刻的理解类的概念,如果这一点没有问题,那么,理解这些代码就不在话下了。对了,今天的代码里有一个比较明显的BUG(我故意放的,当然,肯定还有不少不明显的BUG ),有兴趣的朋友可以当做作业来检验一下自己的水平。
因为今天要掌握的内容比较多,所以,明天暂停一天,给大家一个消化的机会(我也顺便偷个懒)。。。如果有疑问,请用下面的“我要提问”连接提出。
现在轻松啦,可以上床虎虎了。。。
==============================================================
第四天:休息,休息一下
今天大家可要好好消化一下昨天的东西啦。。正好,我也歇歇。对了,有不明白的,点下面的“我要提问”连接提出,我会在论坛里解答。毕竟这里的“我要评论”显示效果差一些,也不能查询。谢谢大家合作 ^ ^
第三天:得到所有的文本单元
wake up!继续啦~~~~~
昨天,我们已经找到了得到一个单元的信息的办法,不过,还没有到实用阶段。毕竟,想实用,还至少要:
对于文本单元,能按名称检索的内容;
对于文件单元,能按名称得到文件的具体内容、类型、全路径、以及大小等信息。
今天,我们就首先着手解决文本单元的问题。
得到内容也许不难,可是,怎么组织才能使这个过程井井有条,才能符合我们的一般习惯?我们可以从现有的知识里找答案。
大家都知道,asp有一个内置对象request,他的功能是得到用户请求的相关信息,包括表单域的值。粗看上去,他的form集合的用法和我们要实现的得到文本单元内容的功能是很近似的,我们来看看request.form的几个应用的例子:
得到表单域的值 -
request.form("表单域名称")或request.form("表单域在<form></form>里的序号")
得到同名表单域的各个元素 -
request.form("表单域名称")(i)或request.form("表单域在<form></form>里的序号")(i)
得到同名表单域的个数 -
request.form("表单域名称").Count或request.form("表单域在<form></form>里的序号").Count
如果我们能够用ourRequest.form("name"),ourRequest.form(index),ourRequest.form("name").count,ourRequest.form(index).count这样的方式,或是与之相近的方式,不就可以很好的和request对象对应起来么?而且,因为对request对象本身的熟悉,也会降低使用我们自己方法的时候的门槛,相对于写一堆getvalue(name)函数这样的方法,更不容易出错,扩展性更好更灵活,可读性也好得多。那么,我们就看看如果要实现自己的request对象,都有哪些工作要做。
首先,ourRequest应该是一个对象,有自己的属性和方法。只有这样,才可能和现有的request对象做呼应。在vbs5里面,已经可以通过Class关键字,来实现自己的类了,所以,可行性上是没有问题的,只要我们自己定义一个类,然后实例化他,就可以得到我们所需的对象;
其次,因为ourRequest.form可以用名称和序号检索,所以,应该提供比较丰富的访问方式;
第三,在表单里有多个域名称相同的时候(比如多个checkbox),应该能够得到其中的各个元素,并且可以得到总个数。所以,ourRequest.form()得到的,应该也是一个可以检索的对象,而且有Count属性。
最终,结合vbscript的语言特点,兼顾开发效率,我们决定实现这样的几个类:
A。UploadRequest
这个类和request对象是对应的
属性:
RawData 得到原始数据,方便检查[只读]
Forms 得到一个有count属性的计数器,
可以用outRequest.Forms.Count的方式,得到文本表单域的的个数[只读]
Form(index) 可以用数字或文本检索文本表单域,做用类似request.form。
他返回一个FormElement型的对象
B。FormElement
可以把它看成单个表单域的化身。通过这个类,可以得到详细的表单域信息,比如name,value等等。如果有多个value(比如checkbox的情况),还可以选择用序号索引
属性:
value 得到表单域的值。如果有多个(比如checkbox),
返回所有的,用逗号分隔[默认]
Name 得到表单域的名称
Item(index) 用数字索引多个值中的某一个
Count 得到对应一个name,所拥有的value的个数。主要用于checkbox[只读]
C。Counter
一个辅助类,就是为了实现outRequest.Forms.Count功能。这里写的并不好,不过考虑大家的理解方便,先暂时这样。
属性:
Count 得到Count
方法:
setCount 设置Count
下面,我们就来看看这几个类的实现:
<%
Class FormElement
’ m_开头,表示类成员变量。
Private m_dicItems
Private Sub Class_Initialize()
Set m_dicItems = Server.CreateObject("Scripting.Dictionary")
End Sub
’ count是咱们这个类的一个只读属性
Public Property Get Count()
Count = m_dicItems.Count
End Property
’ value是一个默认属性。目的是得到值
Public Default Property Get value()
value = Item("")
End Property
’ Name是得到文本域名称。就是<input name=xxx>里的xxx
Public Property Get Name()
Keys = m_dicItems.Keys
Name = Keys(0)
Name = left(Name,instrrev(Name,"_")-1)
End Property
’ Item属性用来得到重名表单域(比如checkbox)的某一个值
Public Property Get Item(index)
If isNumeric(index) Then ’是数字,合法!
If index > m_dicItems.Count Then
err.raise 1,"IndexOutOfBound", "表单元素子集索引越界"
End If
Itms = m_dicItems.Items
Item = Itms(index)
ElseIf index = "" Then ’没给值?那就返回所有的!逗号分隔
Itms = m_dicItems.Items
For i = 0 to m_dicItems.Count-1
If i = 0 Then
Item = Itms(0)
Else
Item = Item & "," & Itms(i)
End If
Next
Else ’给个一个不是数字的东东?出错!
err.raise 2,"IllegalArgument", "非法的表单元素子集索引"
End If
End Property
Public Sub Add(key, item)
m_dicItems.Add key, item
End Sub
End Class
Class UploadRequest
Private m_dicForms
Private m_bFormdata
Private Sub Class_Initialize()
Set m_dicForms = Server.CreateObject("Scripting.Dictionary")
Call fill()
End Sub
’ 有了这个,就可以检查原始数据了
Public Property Get RawData()
RawData = m_bFormdata
End Property
’ 这一段丑陋的代码是为了实现outRequest.Forms.Count这个功能。
Public Property Get Forms()
Set Forms = New Counter
Forms.setCount(m_dicForms.Count)
End Property
Public Property Get Form(index)
If isNumeric(index) Then ’是数字?用数字来检索
If index > m_dicForms.Count Then
err.raise 1,"IndexOutOfBound", "表单元素索引越界"
End If
Items = m_dicForms.Items
Set Form = Items(index)
ElseIf VarType(index) = 8 Then ’字符串?也行!
If m_dicForms.Exists(index) Then ’存在,就返回值
Set Form = m_dicForms.Item(index)
Else ’不存在,就给个空值——request对象就是这么做的。
Exit Property
End If
Else ’给了一个不是数字也不是字符串的东东?出错!
err.raise 2,"IllegalArgument", "非法的表单元素索引"
End If
End Property
Private Sub fill
’ 得到数据
m_bFormdata=request.binaryread(request.totalbytes)
’ 调用这个函数实现递归循环,读取文本单元
Call fillEveryFirstPart(m_bFormdata)
End Sub
Private Sub fillEveryFirstPart(data)
’ 这就是name="
const_nameis=chrb(110)&chrb(97)&chrb(109)&chrb(101)&chrb(61)&chrb(34)
’ 这就是filename="
const_filenameis=chrb(102)&chrb(105)&chrb(108)&chrb(101)&_
chrb(110)&chrb(97)&chrb(109)&chrb(101)&chrb(61)&chrb(34)
’ 这是回车<return>
bncrlf=chrb(13) & chrb(10)
’ 得到divider,分隔符
divider=leftb(data,instrb(data,bncrlf)-1)
’ 起始位置
startpos = instrb(data,divider)+lenb(divider)+lenb(bncrlf)
’ 终止位置,从起始位置开始到下一个divider
endpos = instrb(startpos, data, divider)-lenb(bncrlf)
If endpos < 1 Then ’没有下一个了!结束!
Exit Sub
End If
part1 = midb(data, startpos, endpos-startpos)
’ 得到part1的第一行
firstline = midb(part1, 1, instrb(part1, bncrlf)-1)
’没有filename=",有name=",说明是一个文本单元(这里有一个BUG,自己研究一下?当作业吧)
If Not instrb(firstline, const_filenameis) > 0_
And instrb(firstline, const_nameis) > 0 Then
’ 得到表单域名称,就是<input type=sometype name=somename>里的somename。
fldname = B2S(midb(part1,_
instrb(part1, const_nameis)+lenb(const_nameis),_
instrb(part1, bncrlf)_
-instrb(part1, const_nameis)-lenb(const_nameis)-1))
’ 得到表单域的值
fldvalue = B2S(midb(part1,_
instrb(part1, bncrlf&bncrlf)+lenb(bncrlf&bncrlf),_
lenb(part1)-instrb(part1, bncrlf&bncrlf)+lenb(bncrlf&bncrlf)))
If m_dicForms.Exists(fldname) Then
Set fElement = m_dicForms.Item(fldname)
m_dicForms.Remove fldname
Else
Set fElement = new FormElement
End If
fElement.Add fldname&"_"&fElement.Count, fldvalue
m_dicForms.Add fldname, fElement
End If
’ 截取剩下的部分,递归调用这个函数,来得到下一个part1。
Call fillEveryFirstPart(rightb(data, lenb(data)-endpos-1))
End Sub
’ 这是一个公用函数,作用是二进制和字符串的转换
Private Function B2S(bstr)
If not IsNull(bstr) Then
for i = 0 to lenb(bstr) - 1
bchr = midb(bstr,i+1,1)
If ascb(bchr) > 127 Then ’遇到了双字节,就得两个字符一起处理
temp = temp & chr(ascw(midb(bstr, i+2, 1) & bchr))
i = i+1
Else
temp = temp & chr(ascb(bchr))
End If
next
End If
B2S = temp
End Function
End Class
’ 这是一个辅助类,为了实现outRequest.Forms.Count功能。
Class Counter
Private m_icnt
’ count是咱们这个类的一个只读属性
Public Property Get Count()
Count = m_icnt
End Property
Public Function setCount(cnt)
m_icnt = cnt
End Function
End Class
%>
<%
’下面是测试码
set outRequest = new UploadRequest
%>
<%=outRequest.Form(0).Name%>:<%=outRequest.Form("file1_desc")%><br>
<%=outRequest.Form(1).Name%>:<%=outRequest.Form("file2_desc")%><br>
<%=outRequest.Form(2).Name%>:<%=outRequest.Form(2).Count%><br>
<%=outRequest.Form(3).Name%>:<%=outRequest.Form(3)%><hr>
一共有<%=outRequest.Forms.Count%>个文本单元
这里的注释很详细,而且,每一个类的属性和方法都很少,所以相信基础好的朋友读懂是没有问题的。对应的,我们的测试表单也改成了:
<form action="doupload.asp" method=post enctype="multipart/form-data">
file1说明:<input type=text name=file1_desc>
file1<input type=file name=file1><br>
file2说明:<input type=text name=file2_desc>
file2<input type=file name=file2><br>
<input type=checkbox name=chk value=a>a
<input type=checkbox name=chk value=b>b
<input type=checkbox name=chk value=c>c
<input type=checkbox name=chk value=d>d
<input type=checkbox name=chk value=e>e<hr>
<input type=submit name=upload value=upload>
</form>
注意,这里的每一个文本表单域都要填上,因为测试码给得很特殊,读了0,1,2,3各个项目的值,测试了各个属性。不过,现实情况下,因为事先知道表单域的名称;即使不知道,也可以用outRequest.Forms.Count来循环读取,所以是没问题的,不容易出错。
现在,试试看!怎么样?成功了吧 呵呵,中英文都没有问题,用法也很简单,很清晰。现在,我们就可以说基本上解决了文本域的读取问题。
--------------------------------------------------------
今天这一段是很有挑战性的。我写了两个多小时。对于尚处于初级的朋友,可能会觉得有些吃力。其实,关键在于深刻的理解类的概念,如果这一点没有问题,那么,理解这些代码就不在话下了。对了,今天的代码里有一个比较明显的BUG(我故意放的,当然,肯定还有不少不明显的BUG ),有兴趣的朋友可以当做作业来检验一下自己的水平。
因为今天要掌握的内容比较多,所以,明天暂停一天,给大家一个消化的机会(我也顺便偷个懒)。。。如果有疑问,请用下面的“我要提问”连接提出。
现在轻松啦,可以上床虎虎了。。。
==============================================================
第四天:休息,休息一下
今天大家可要好好消化一下昨天的东西啦。。正好,我也歇歇。对了,有不明白的,点下面的“我要提问”连接提出,我会在论坛里解答。毕竟这里的“我要评论”显示效果差一些,也不能查询。谢谢大家合作 ^ ^