欢迎光临
我们一直在努力

ASP无组件上传·从原理剖析到实践(中)-ASP教程,ASP应用

建站超值云服务器,限时71元/月

第五天:得到文件单元

今天我们要进行的部分,是比较有趣味性地——得到文件内容。其实,看看我们的要处理的数据,再看看前天文本单元的处理,相信大家也会心中有数。

为了清晰的区分文件和文本单元,这一次,我们用ourrequest.file(index)来对应文本单元的ourrequest.form(index)。当然,因为对于文件,我们需要得到的信息不同于文本,所以这次得到的,也不会是formelement,而是一个新对象fileelement。

文件单元和文本单元在原始数据上,不同点少得可怜:

1。第一行多了一个filename="xxx"模块;

2。多了一个用于指示contenttype的第二行。

感兴趣的目标信息不同,所以,得到的对象fileelement也和formelement有一些不同点:

1。不需要count属性(不存在checkbox情况);

2。不需要item(index)(同上,不存在checkbox情况);

3。需要一个contenttype属性;

4。需要一个filepath属性;

5。需要一个filename属性;

6。需要一个size属性;

7。因为需要的是二进制,所以,没有必要进行二进制=>字符串的转换;

8。因为需要的是二进制,所以,属性value改成data更合适

此外,uploadrequest也应该相应的添加files属性、form(index)方法、以及m_dicfiles成员。现在,我们就来扩充他:

a。uploadrequest(上面设计过,这里是扩充)

这个类和request对象是对应的

属性:

rawdata 得到原始数据,方便检查[只读]

forms 得到一个有count属性的计数器,

可以用outrequest.forms.count的方式,得到文本表单域的的个数[只读]

files 得到一个有count属性的计数器,

可以用outrequest.files.count的方式,得到文件表单域的的个数[只读]

form(index) 可以用数字或文本检索文本表单域,做用类似request.form。

他返回一个formelement型的对象

file(index) 可以用数字或文本检索文件表单域,他返回一个fileelement型的对象

b。fileelement

可以把它看成单个文件域的化身。通过这个类,可以得到详细的文件信息,比如name,data,path,filename,contenttype,size等等。

属性:

name 文件域的名称。就是<input type=file name=xxx>里的xxx

data 文件域的内容。二进制串

contenttype 文件域的contenttype

filepath 文件域包含的文件在客户机上的全路径

filename 文件域包含的文件的文件名

size 文件域包含的文件的尺寸

这里是实现。还是存成doupload.asp:

<%

=========================================================================

这个,是存储文本域信息的的类。每一个name的文本域,对应一个这样的类。

=========================================================================

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-1 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

=========================================================================

这个,是存储文件域信息的的类。每一个name的文件,对应一个这样的类。

=========================================================================

class fileelement

m_开头,表示类成员变量。

private m_strname

private m_bdata

private m_strcontenttype

private m_strfilepath

private m_strfilename

private m_lsize

data是一个默认属性。目的是得到值

public default property get data()

data = m_bdata

end property

name是得到文件域名称,就是<input type=file name=xxx>里的xxx

public property get name()

name = m_strname

end property

contenttype是得到文件contenttype

public property get contenttype()

contenttype = m_strcontenttype

end property

filepath是得到文件在客户端的路径

public property get filepath()

filepath = m_strfilepath

end property

filepath是得到文件在客户端的路径

public property get filename()

filename = m_strfilename

end property

size是得到文件大小

public property get size()

size = m_lsize

end property

public sub add(name, data, contenttype, path)

m_strname = name

m_bdata = data

m_strcontenttype = contenttype

m_strfilepath = path

m_strfilename = right(path, len(path)-instrrev(path, "\"))

m_lsize = lenb(data)

end sub

end class

=========================================================================

这个,是我们模拟的request类。我们用它完成asp的request完成不了的任务 🙂

=========================================================================

class uploadrequest

private m_dicforms

private m_dicfiles

private m_bformdata

private sub class_initialize()

set m_dicforms = server.createobject("scripting.dictionary")

set m_dicfiles = 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

这一段丑陋的代码是为了实现outrequest.files.count这个功能。

public property get files()

set files = new counter

files.setcount(m_dicfiles.count)

end property

public property get form(index)

if isnumeric(index) then 是数字?用数字来检索

if index > m_dicforms.count-1 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

public property get file(index)

if isnumeric(index) then 是数字?用数字来检索

if index > m_dicfiles.count-1 then

err.raise 1,"indexoutofbound", "文件元素索引越界"

end if

items = m_dicfiles.items

set file = items(index)

elseif vartype(index) = 8 then 字符串?也行!

if m_dicfiles.exists(index) then 存在,就返回值

set file = m_dicfiles.item(index)

else 不存在,出错!

err.raise 2,"nullref", "文件元素索引不存在"

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

有filename=",有name=",说明是一个文件单元(这里还是有一个bug,研究出来没?)

elseif instrb(firstline, const_filenameis) > 0_

and instrb(firstline, const_nameis) > 0 then

得到表单域名称,就是<input type=file name=somename>里的somename。

fldname = b2s(midb(part1,_

instrb(part1, const_nameis)+lenb(const_nameis),_

instrb(part1, const_filenameis)_

-instrb(part1, const_nameis)-lenb(const_nameis)-3))

得到表单域的值

fldvalue = midb(part1,_

instrb(part1, bncrlf&bncrlf)+lenb(bncrlf&bncrlf),_

lenb(part1)-instrb(part1, bncrlf&bncrlf)+lenb(bncrlf&bncrlf))

得到路径

filepath = b2s(midb(part1,_

instrb(part1, const_filenameis)+lenb(const_filenameis),_

instrb(part1, bncrlf)_

-instrb(part1, const_filenameis)-lenb(const_filenameis)-1))

得到contenttype

contenttype = b2s(midb(part1,_

instrb(part1, bncrlf)+lenb(bncrlf)+14,_

instrb(part1,_

bncrlf&bncrlf)-instrb(part1, bncrlf)-lenb(bncrlf)-14))

if lenb(fldvalue) > 0 then size>0说明有文件传来了。

if m_dicfiles.exists(fldname) then

set felement = m_dicfiles.item(fldname)

m_dicfiles.remove fldname

else

set felement = new fileelement

end if

felement.add fldname, fldvalue, contenttype, filepath

m_dicfiles.add fldname, felement

end if

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)%>

一共有<%=outrequest.forms.count%>个文本单元<hr>

<%=outrequest.file(0).name%>:

<%=outrequest.file("file1").contenttype%>:

<%=outrequest.file("file1").size%>byte:

<%=outrequest.file("file1").filename%>:

<%=outrequest.file("file1").filepath%><br>

<%=outrequest.file(1).name%>:

<%=outrequest.file("file2").contenttype%>:

<%=outrequest.file("file2").size%>byte:

<%=outrequest.file("file2").filename%>:

<%=outrequest.file("file2").filepath%><br>

一共有<%=outrequest.files.count%>个文件单元<hr>

<%

如果要测试文件1内容,可以:

response.clear

response.contenttype = outrequest.file("file1").contenttype

response.binarywrite(outrequest.file("file1").data)

如果要测试文件2内容,可以:

response.clear

response.contenttype = outrequest.file("file2").contenttype

response.binarywrite(outrequest.file("file2").data)

%>

测试表单testform.html还用第三天那个。注意,每一个文本表单域和文件表单域都要填上,原因还是测试码给得很特殊,读了各个项目的值,测试了各个属性。不过,现实情况下,因为事先知道表单域的名称;即使不知道,也可以用outrequest.forms.count/outrequest.files.count来循环读取,所以是没问题的,不容易出错。

试试看!怎么样?成功!注意测试码最下边的部分,用来测文件内容的。分了两段,可以分别打开注释,进行测试哦。

现在,中英文文本都没有问题;文件也是各种都行,路径没限制。用法也很简单,很清晰。现在,文本域、文件域的读取就都解决了!

——————————————————–

今天这一段是很有意思的。结合第三天的内容,就可以看到解决upload问题的全貌了。这两次我都是越写越兴奋,放不下。不过,这可还没有结束哦!要做一个功能强大的东东出来,还需要限制上传文件尺寸、限制类型、存盘、入库等附加功能。所以,打起精神,让我们一鼓作气,把他彻底搞定!哦?!现在都一点啦,明天吧,呵呵。。。

==============================================================

第六天:附加功能

现在,核心功能已经实现了。但是,仅仅这样,还不能大幅度的提高我们的工作效率。一些常用的、重复的操作,象限制上传文件大小、类型以及文件存盘、入库等还是应该统一处理。所以,当前的目标,就是封装常用的功能,尽量让他好用。

首先,我们看看上传限制的实现。我们要控制的,有文件大小,和文件类型。大小很容易控制,只要在每次读取文件放进fileelement类的时候(执行add方法的时候),看一下它的size,并且适时的抛出异常就可以了;文件类型控制类似,不过需要判断一下扩展名(当然也可以利用contenttype,不过更依赖机器配置——每一种机器可以识别的contenttype各有不同)。

然后,就是结果的永久性保存了。为了灵活起见,入库就不再封装,由用户解决;存盘因为它是原子操作(本身不可分割,而且也不依赖其他操作),可以比较好的封装。我们可以在uploadrequest类提供saveto(serverpath)方法,用来一次性保存所有图片;另外在fileelement类里提供saveto(serverpath)和saveas(serverpath, newfilename)方法,分别实现按照原文件名保存图片以及按照指定文件名保存文件的功能。

考虑到上传控制的问题,把读取数据的fill方法放到class_initialize已经不合适了。我们另做一个upload方法,进行文件上传的具体操作。这样,就可以在上传之前,对ourrequest进行设置。新的类设计如下:

a。uploadrequest(上面设计过,这里是扩充)

这个类和request对象是对应的

属性:

rawdata 得到原始数据,方便检查[只读]

forms 得到一个有count属性的计数器,

可以用outrequest.forms.count的方式,得到文本表单域的的个数[只读]

files 得到一个有count属性的计数器,

可以用outrequest.files.count的方式,得到文件表单域的的个数[只读]

form(index) 可以用数字或文本检索文本表单域,做用类似request.form。

他返回一个formelement型的对象[只读]

file(index) 可以用数字或文本检索文件表单域,他返回一个fileelement型的对象[只读]

totalbytes 得到所有文件总大小[只读]

allowedfileslist 设置允许上传的扩展名[只写]

deniedfileslist 设置不允许上传的扩展名(和allowedfileslist任取一个就行了)[只写]

maxfilesize 设置允许上传的每个文件的大小[只写]

totalmaxfilesize 设置允许上传的所有文件的大小[只写]

方法:

upload 上传分拆的具体实现方法

saveto(path) 保存所有的文件到指定路径(按原名)

b。fileelement

可以把它看成单个文件域的化身。通过这个类,可以得到详细的文件信息,比如name,data,path,filename,contenttype,size等等。

属性:

name 文件域的名称。就是<input type=file name=xxx>里的xxx[只读]

data 文件域的内容。二进制串[只读]

contenttype 文件域的contenttype[只读]

filepath 文件域包含的文件在客户机上的全路径[只读]

filename 文件域包含的文件的文件名[只读]

size 文件域包含的文件的尺寸[只读]

方法:

saveto(path) 保存当前文件到指定路径(按原名)

saveas(path, name) 按给定文件名保存当前文件到指定路径,如果存在,就覆盖

savewithoutoverwrite(path, name) 按给定文件名保存当前文件到指定路径,不覆盖

保存文件的时候,因为它是二进制流,所以,只能用于文本操作的fso是不能用的,这里,我们用到了ado的stream对象,他也是唯一的选择。但是,一定要注意,因为新版ado才有他,所以,老的系统可能不能正确的进行文件的保存。如果提示了stream对象的问题,请升级mdac,或者干脆放弃这个功能。因为我们之所以用无组件的方法,就是不想在server上配置。装mdac本身已经偏离了目标。

现在,只要实现了这几个新方法和属性,我们的无组件上传就可以说是大功告成了。明天,我们就最终实现这个功能完善的类,并且把前两天没有注意到的细节进行一些修补。其实明天的内容不多,新的知识只有stream对象的用法。这些,在论坛里以前就有提及,如果不是很清楚,可以翻看一下论坛的旧贴,或是到http://www.2yup.com/asp/referrence/index.asp下一个ado参考看看,相信会揭开你的心中疑团。

ok,让我们一起期待明天吧! ^&^

赞(0)
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com 特别注意:本站所有转载文章言论不代表本站观点! 本站所提供的图片等素材,版权归原作者所有,如需使用,请与原作者联系。未经允许不得转载:IDC资讯中心 » ASP无组件上传·从原理剖析到实践(中)-ASP教程,ASP应用
分享到: 更多 (0)