原创:简单的ASP无组件上传原理实践总结!

Posted on 2008-02-25 08:37  蓝箭GZ  阅读(2384)  评论(1编辑  收藏  举报
首先是网上的一个经典上传代码:
一.submit.htm
<html><title>example</title>
<body>
<form name="form1" method="post" action="upload.asp" enctype="multipart/form-data">
<input type=file name="myfile">
<input type=submit name="submit" value="
提交">
</form>
</body>
</html>

二.upload.asp
<%@ Language=VBScript %>
<%
function binarytoasi(byval varstr)
   asi=""
   for i=1 to 3
       asi=asi&chr(ascb(midb(varstr,i,1)))
   next
   binarytoasi=asi
end function

formsize=request.totalbytes
formdata=request.binaryread(request.totalbytes ) '
获得上传数据
bcrlf=chrB(13) & chrB(10)
divider=leftB(formdata,clng(instrb(formdata,bcrlf))-1)

Position=instrb(formdata,bcrlf & bcrlf)+4        '
上传文档数据开始字节位置
filesize=instrb(Position+1,formdata,divider)-Position-4 '
上传文档的总长度    注:这个位置有错,应改成-2
exnamestart=instrb(1,formdata,chrb(46),1)+1       ' 取扩展名的开始位置
exnameend=instrb(exnamestart,formdata,chrb(34),1)  ' 取扩展名的结束位置
exname=midb(formdata,exnamestart,exnameend-exnamestart)     ' 取扩展名

set dr=CreateObject("Adodb.Stream")
    dr.Mode=3: dr.Type=1:dr.Open
set dr1=CreateObject("Adodb.Stream")
    dr1.Mode=3:dr1.Type=1:dr1.Open  

dr.Write formdata
dr.Position=Position-1
dr.CopyTo dr1,filesize
dr1.SaveToFile "d:/test/temp_"&"."&binarytoasi(exname),2    '
文档保存
set dr=nothing:set dr1=nothing
%>

分析:第一.
上传通过:<input type=file name="myfile">以及<form name="form1" method="post" action="upload.asp" enctype="multipart/form-data">中的enctype="multipart/form-data" 这种方式,就会在提交时产生如下FORM包括的控件及文件myfiler的内容:

注:<input ...>的TYPE的值有:
button  checkbox  file  hidden  image  password  radio  reset  submit  text


原TXT文件内容:
2008好运之年![坚师傅电脑维修中心]正式开张,电脑杀毒、远程维护、家庭组网....统一价:38元(新张8折)。

通过在upload.asp中<%Response.BinaryWrite FormData %>输入提交后接收到的所有内容

-----------------------------7d81191c10748 Content-Disposition: form-data; name="file1"; filename="D:\Documents and Settings\Administrator\My Documents\2008好运之年.txt" Content-Type: text/plain 2008好运之年![坚师傅电脑维修中心]正式开张,电脑杀毒、远程维护、家庭组网....统一价:38元(新张8折)。 -----------------------------7d81191c10748 Content-Disposition: form-data; name="submit" 提交 -----------------------------7d81191c10748--

如何取得文件的开始字节位置:
红色字部分是一个分隔符,每次长度及名称都不同,但同一次提交3个是相同的,而且通过WINHEX软件查看到文章内容的开始处肯定有0D0A0D0A(蓝色字中间的空格的内容的二进制值)如图:




所以通过:

bcrlf=chrB(13) & chrB(10)    '0D0A的二进制值

Position=instrb(formdata,bcrlf & bcrlf)+4

注:InStr 函数

InStr([start, ]string1, string2[, compare])

从start位置开始找出string2在string1的第一个位置,compare有0(不写就默认=0)=执行二进制比较,1=执行文本比较(我理解,0就是比较时区分大小写,1就是不区分大小写)

找到0D后要+4个字节才是文章的开始字节。

如何取得文件的长度:
试过通过文件结束处的0D0A,用Fileend=instrb(Position+1,formdata,bcrlf)-1,找到文件最后一个字节
文件长度=Fileend-Position+1
有个问题就是小文件可以,大文件时,文件中可能有许多0D0A,这样就出错了!

正确方法是:找到文件内容后的分隔符位置,再-2(原程序中-4不知是什么原因,会造成txt,jpg文件(少了两个字节)上传后打开正常,而RAR,EXE,GIF就打开出错)再-文件开始位置,即:

filesize=instrb(Position+1,formdata,divider)-2-Position

如何提取分隔符的字串值:

divider=leftB(formdata,clng(instrb(formdata,bcrlf))-1)

instrb(formdata,bcrlf) 定位在第一个0D0A的OD上,-1就定位在分隔符的最后一个字节上。
注:leftB(str,num) 从左边起取str的多少个字节, clng(str)将字串转成Long型数值。

为什么要set dr=CreateObject("Adodb.Stream")set dr1=CreateObject("Adodb.Stream")
要产生两个Adodb.Stream对象

由于SaveToFile方法,只能全部保存通过dr.Write formdata 写入缓冲区的数据,而formdata不只是文件的数据,可通过copyto方法(可以从dr.position开始的,长度为filesize的数据)将所要用的文件数据利用dr作源,复制到目的dr1,将生成文件。

为什么要dr.position=position-1 而不是dr.position=position 因为position就是文件的第一个字节了!

经实践发现,生成Adodb.Stream对象,文件指针位置为0,无论读写文件,都是先将文件指针position+1后再读写字节数据,所以要生成文件前,要将文件指针先-1!

常见的两个错误:
1.当上传文件超过200KB时,报错:“请求对象错误 'ASP 0104 : 80004005' 操作被禁止 /Upload.asp, line 40 ”
因IIS6限制了上传大小为200KB
解决:先将IIS属性中的“允许直接编辑配置数据库”勾选


再找到 XX:\Windows\System32\Inetsrv 中的 metabase.XML,  修改 AspMaxRequestEntityAllowed=204800(200KB)。


2.当文件上传限制已改为200MB后,上传文件大于4MB时,报错:“Response 对象 错误 'ASP 0251 : 80004005'
    
    超过响应缓冲区限制       /showuser.asp,行 0     此 ASP 页的执行造成响应缓冲区超过其配置限制。”

解决办法:找到 XX:\Windows\System32\Inetsrv 中的 metabase.XML,  修改 AspBufferingLimit="4194304"(4MB)。



Copyright © 2024 蓝箭GZ
Powered by .NET 9.0 on Kubernetes