答案:无组件上传一直是困扰大家的一个问题。其实原理很简单,核心就是分析字符串。但是,实际操作时,却困难重重。其中的关键问题还是大家往往对原理的剖析不够深入,或者是因为过程过于繁琐,导致bug不断。一直以来,都想做一个完善的例子,只不过想想就头痛,加上没时间(借口,呵呵 ),所以没有付诸行动。
今天就咬咬牙,给大家提供一个完整的无组件上传的例子。因为本人耐性不好,所以咱们一点一点来,分几天完成。未来的几天,我会天天更新这个文档,这个过程也是大家学习和提高的过程。
(完整的源码和示例,可以在这里找到:http://www.2yup.com/asp/attach/A0000006.zip)
==============================================================
第一天:认识我们的解剖对象——数据
上传文件时,首先要知道我们得到的是什么。下面是一个上传文件的表单,我们就从他开始。
<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=submit name=upload value=upload>
</form>
表单中enctype="multipart/form-data"的意思,是设置表单的MIME编码。默认情况,这个编码格式是application/x-www-form-urlencoded,不能用于文件上传;只有使用了multipart/form-data,才能完整的传递文件数据,进行下面的操作(有兴趣的朋友,可以自己试试看两者的异同。方法很简单,就是把这一句去掉)。现在,我们在表单中分别填入数据:
file1的说明 D:\我的 图片\BACK046.GIF
file2的说明 D:\我的 图片\BACK293.GIF
这里用了中英文、空格混排。目的是让例子更有一般性。我选的这两个图片分别是54和62字节。大图片的原理完全一样,不过小图片做例子更合适些,原理容易展现。
为了看到我们得到的数据,在doupload.asp里,有这几行代码:
<%
formsize=request.totalbytes
formdata=request.binaryread(formsize)
response.BinaryWrite(formdata)
%>
很简单,作用就是打出来传过来的所有数据。如果不熟悉,你可以先研究一下request和response对象的这两个方法。
提交表单,我们在ie里面查看html源,得到:
-----------------------------7d22131090458
Content-Disposition: form-data; name="file1_desc"
file1μ??μ?÷
-----------------------------7d22131090458
Content-Disposition: form-data; name="file1"; filename="D:\?òμ? í???\BACK046.GIF"
Content-Type: image/gif
GIF89a‘ì?f?f3?ì???ì!ù,@?.á?o ;
-----------------------------7d22131090458
Content-Disposition: form-data; name="file2_desc"
file2μ??μ?÷
-----------------------------7d22131090458
Content-Disposition: form-data; name="file2"; filename="D:\?òμ? í???\BACK293.GIF"
Content-Type: image/gif
GIF89a(‘???YYYììì!ù,(@L€?j(·"j?N(34ˉ;
-----------------------------7d22131090458
Content-Disposition: form-data; name="upload"
upload
-----------------------------7d22131090458--
不用怀疑,这就是你从上一个“简单”表单传过来的东西。现在想想看,怎么对付这一堆东西?是不是看上去有规律,又不知道从何下手?明天,咱们就分析一下这堆“图片”,看看怎么分离出我们要的内容。
==============================================================
第二天:分拆初步
睡了个好觉,大家脑子清醒多了吧?今天中午吃的火锅,阿森纳vs.铁哥也没看完,现在一脑子大油。。。
OK,咱们继续研究这个枯燥的问题。首先,要找出规律。看上去似乎很简单,就是用
-----------------------------7d22131090458
做分隔,这样,每一个文本单元里,都是
Content-Disposition: form-data; name="表单域的名字";
表单域的内容
而每一个文件单元里,都是
Content-Disposition: form-data; name="表单域的名字"; filename="文件全路径"
Content-Type: 文件类型
文件的二进制内容
那么,是不是直接用
split(formdata,"-----------------------------7d22131090458")
就可以得到各个单元了呢?答案是否定的。首先,formdata不是字符串而是二进制串,不能用split的方法;其次,这里的7d22131090458并不固定,每次都会有变化,并不适合做分隔符。所以,应该用一个更保险的办法。想到没?很简单——就用formdata的第一行做分隔符。只要用instrb函数得到换行符的位置,然后用leftb或midb函数截取数据就行了。我们动手试试:
<%
' 二进制的回车<return>
bncrlf=chrB(13) & chrB(10)
' 得到formdata
formsize=request.totalbytes
formdata=request.binaryread(formsize)
' 得到分隔符
divider=leftB(formdata,clng(instrb(formdata,bncrlf))-1)
' 看看对不对?
response.BinaryWrite(divider)
%>
运行。。。成功了!得到了需要的divider。注意,这里的字符串函数都是针对二进制数据操作的,所以,用的是他们的二进制版,加了“b”(binary的首字母)——instrb,leftb(以后可能还出现rightb,midb,lenb。。等等)。毕竟,formdata是用“binaryread()”得到的嘛。好了,有的分隔符,就可以得到数据了。我们从简单的开始,先拿第一个单元出来看看,目标是得到表单域名称和数据。
<%
' 这是回车<return>
bncrlf=chrB(13) & chrB(10)
' 得到数据
formsize=request.totalbytes
formdata=request.binaryread(formsize)
' 得到divider,分隔符
divider=leftB(formdata,clng(instrb(formdata,bncrlf))-1)
' 起始位置
startpos = instrb(formdata,divider)+lenb(divider)+lenb(bncrlf)
' 终止位置,从起始位置开始到下一个divider
endpos = instrb(startpos, formdata, divider)-lenb(bncrlf)
part1 = midb(formdata, startpos, endpos-startpos)
response.BinaryWrite(part1)
%>
这一段有注释,相信大家没问题。如果对这些函数不了解,可以到http://www.2yup.com/asp/referrence/index.asp下载msdn参考看看vbscript的函数用法,对提高水平有很大帮助。
这时候得到的结果可以通过查看生成的html源的方式看到:
Content-Disposition: form-data; name="file1_desc"
file1的说明
好了,离成功又进一步!
下来只要分别读取part1里name="和第一个“双引号+回车”之间的内容就可以得到表单域的名称;读取连续两个回车之后的内容就可以得到表单域的值了。下面一段顺理成章:
<%
' 这就是name="
const_nameis=chrb(110)&chrb(97)&chrb(109)&chrb(101)&chrb(61)&chrb(34)
' 这是回车<return>
bncrlf=chrB(13) & chrB(10)
' 得到数据
formsize=request.totalbytes
formdata=request.binaryread(formsize)
' 得到divider,分隔符
divider=leftB(formdata,clng(instrb(formdata,bncrlf))-1)
' 起始位置
startpos = instrb(formdata,divider)+lenb(divider)+lenb(bncrlf)
' 终止位置,从起始位置开始到下一个divider
endpos = instrb(startpos, formdata, divider)-lenb(bncrlf)
part1 = midb(formdata, startpos, endpos-startpos)
' 得到表单域名称,就是<input type=sometype name=somename>里的somename。
fldname = midb(part1,_
instrb(part1, const_nameis)+lenb(const_nameis),_
instrb(part1, bncrlf)-instrb(part1,const_nameis)-lenb(const_nameis)-1)
' 得到表单域的值
fldvalue = midb(part1,_
instrb(part1, bncrlf&bncrlf)+lenb(bncrlf&bncrlf),_
lenb(part1)-instrb(part1, bncrlf&bncrlf)+lenb(bncrlf&bncrlf))
' 检查一下?可以每次打开一个注释,分别检查。
'response.binarywrite(fldname)
'response.binarywrite(fldvalue)
%>
执行一下?呵呵,没问题啦,分别打开注释,会在IE里看到“file1_desc”和“file1的说明”。
当然,这是得到文本单元的方法,不过看看上边的原始数据就知道,得到文件单元方法可以说是基本相同,只不过:
1。需要额外得到filename=""里的值,也就是文件全路径;
2。需要额外得到Content-Type: 后边的值,也就是文件的类型。
这个工作就是体力劳动了,相信大家没问题。现在更大的精力应该放在:怎么得到所有的段落内容?想来应该是某种形式的循环,但是,具体怎么做?还有,怎么样组织得到的东西,才不显得凌乱?
呵呵,不早了,这个就是咱今天晚上要做的梦了。明天来,咱就一起解决这个问题。。。。
==============================================================
第三天:得到所有的文本单元
wake up!继续啦~~~~~
昨天,我们已经找到了得到一个单元的信息的办法,不过,还没有到实用阶段。毕竟,想实用,
上一个:ASP无组件上传·从原理剖析到实践(中)
下一个:ASP编写的一个简单的颜色吸取器